注意:如果你是用的是V4包中的API.你需要
1.Activity必須繼承FragmentActivity
2.使用getSupportFragmentManager()而不是getFragmentManager().
Fragment
Fragment代表了Activity中ui的一個獨立行爲,或者說一個區域.你可以在Activity中組合多個Fragment去打造多面板的ui,你也能夠在多個Activity中重用同一個Fragment.你可以把Fragment想象成Activity的一個擁有獨立生命週期並且可以獨立的接受輸入事件的模塊,你也能夠把它加入或移除一個正在運行的Activity(某種程度上像一個你能夠在多個Activity中重用的子Activity).
Fragment 必須被嵌入一個Activity(以下稱爲宿主Activity)中,且Fragment的生命週期,直接被其宿主Activity所影響.比如說,當Activity進入paused狀態,所有子Fragment也會paused.Activity被Destroy. 所有子Fragment也會跟着被銷燬.不僅如此,當Activity正在運行(也就是resumed狀態)時,你依然可以獨立操縱每個單獨的Fragment,例如增加和移除它們.當你增刪它們的時候,你可以把它們加入一個由宿主Activity所管理的回退棧中.每個回退棧記錄了你對Fragment所進行的事務.回退棧使得用戶可以通過點擊返回按鈕返回上一個對Fragment進行的事務.
當你把一個Fragment作爲你Activity的一部分增加到佈局中時,它是以Activity的view中一個子View的形式存在的.你可以在Activity佈局中用一個<Fragment>元素去聲明一下,這樣你就在Activity中插入了一個Fragment.你同樣也可以在代碼中將其動態添加到某個ViewGroup裏面.但是這並不表示Fragment必須要成爲Activity佈局的一部分,你也能加入一個沒有UI界面的Fragment當成”隱形工人”來使用.
這篇文檔主要描述如何去使用Fragment搭建你的APP.具體包括:
1. Fragments在被加入回退棧時怎麼去保持它們的狀態.
2. Fragment和Activity的通信,Fragments之間的通信.
3. 怎麼更好的搭配Actionbar的使用等等.
Creating a Fragment
爲了創建一個Fragment,你必須創建一個類去繼承Fragment(或者使用一個系統定義的Fragment的子類).這個Fragment類的代碼看起來非常像Activity.它包含了一些和Activity類似的回調方法,例如 onCreate(), onStart(), onPause()和 onStop().
事實上,如果你正在用Fragment重構你的app代碼,你要做的僅僅是把你在Activity的回調方法裏的代碼搬運到Fragment的回調方法裏面.
通常,你至少需要實現下列方法:
onCreate()
當系統創建Fragment的時候會回調這個方法.在你的實現方法裏,你應該初始化那些Fragment中必不可少的組件,以便於當Fragment經歷Paused,stoped最終resume的時候,你能保留他們.
onCreateView()
當Fragment第一次去畫他的UI的時候,系統會回調這個方法.你必須在這個方法裏返回你的Fragment的根佈局的View實例以使系統創建Fragment的UI.如果Fragment沒有界面,你就返回null.
onPause()
系統調用這個方法作爲用戶離開Fragment的首要標識(但是,這並不意味着Fragment被銷燬).你應當在這裏保存用戶操作引起的參數(比如一些設置)改變.因爲用戶可能不會再返回了
大多數app應當給每個Fragment至少實現這裏列出的三個方法,但是還有一些其他的回調函數能用來處理Fragment的各種生命週期狀態.
我們也提供了幾個你可能想去繼承的子類,以替代Fragment基類:
DialogFragment
展示一個浮動的對話框.使用這個類去創建一個對話框是一個在Activity類中替換dialoghelper方法創建對話框的好選擇,因爲你能在Activity的Fragment回退棧中放入一個Fragment對話框,受Activity的管理.這樣用戶就可以點返回返回剛纔的對話框了.
ListFragment
展示一列被adapter關聯的條目,類似於listActivity.它提供了幾個方法去管理一個listView.例如處理點擊事件的onListItemClick()回調函數.
PreferenceFragment
用列表展示一層偏好設置,類似於 PreferenceActivity .這樣你在做一些設置界面的時候就會很方便.
Adding a user interface
Fragment通常被用作Activity 界面的一部分並且向Activity提供它自己的佈局.想給Fragment提供一個佈局,你必須實現oncreateView()回調方法.安卓系統會在繪製Fragment的界面的時候回調它.你對這個方法的實現,必須返回一個View實例.這個實例應該是Fragment佈局的根佈局.
提示:如果你的Fragment是listFragment的子類,默認實現就返回了一個listView,所以你就不必去實現了.
你可以從資源文件裏inflate一個XML文件出一個View作爲你的Fragment的佈局,爲了幫你實現這一點,oncreateView()方法提供了一個LayoutInflater給你使用.
下列代碼中,Fragment的實現類從example_Fragment.xml中加載了一個佈局.
public static class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this Fragment
return inflater.inflate(R.layout.example_Fragment, container, false);
}
}
被傳入oncreateView的Container參數是在Activity佈局中指定的父容器,你的Fragment應該被插入到這個container裏面. 如果這個Fragment是被恢復狀態的話,那麼savedInstanceState參數就是一個提供了之前Fragment實例數據的數據包,.Inflate方法接收三個參數:
1.你想inflate的資源ID
2.你將要壓縮的這個佈局的父佈局.在這裏傳入container是非常重要的,這可以方便系統給你所inflate的這個佈局的根佈局設置由父View指定的參數.(譯者注:這裏不傳container你的xml文件中的某些屬性會失效,原因請搜索郭霖大神的博文)
3.一個用來標識inflate的這個佈局是否要被附到Viewgroup(就是第二個參數)的布爾值.
現在你已經瞭解了怎麼創建一個有佈局的Fragment.
下一步,你需要把它添加到你的Activity中.
Adding a fragment to an activity
方式一:在xml文件裏聲明在下面這個例子裏,你能像一個普通的View一樣爲Fragment指定佈局屬性.如下例子:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<Fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
<Fragment>中的android:name屬性指定了Fragment的類名來初始化佈局.
當系統創建Activity佈局的時候,它會初始化每個在xml中指定的Fragment,並調用每個Fragment的oncreateView()方法來取得每個Fragment的佈局.系統會將Fragment返回的View直接插入<Fragment>元素中.
提示:每個Fragment需要一個特定的標識使得系統在Activity重新啓動時能用來恢復Fragment.(你也可以用來獲得Fragment去做一些事務,例如移除它),有三種方式可以給Fragment提供一個ID.
1. 指定android:id屬性
2. 指定android:tag屬性
3. 如果你沒有使用前兩種,系統會使用containerView的ID.
方式二:用編程的方式將Fragment增加到現存的ViewGroup中
在你的Activity運行的任何時刻,你都能增加Fragment到你的Activity佈局中.你只需要簡單的指定一個用來容納Fragment的Viewgroup.
想在你的Activity中使用Fragment事務,例如增加和移除的事務,你必須使用來自FragmentTransaction的API.你可以使用如下代碼從你的Activity中得到一個FragmentTransaction的實例:
FragmentManager FragmentManager = getFragmentManager()
FragmentTransaction FragmentTransaction = FragmentManager.beginTransaction();
你能使用add()方法增加一個Fragment,同時說明他要被插入到哪個View中.例如:ExampleFragment Fragment = new ExampleFragment();
FragmentTransaction.add(R.id.Fragment_container, Fragment);
FragmentTransaction.commit();
第一個傳入add()函數的參數是Fragment將要插入的Viewgroup對象,使用資源ID來指定,第二個參數就是要增加的Fragment.一旦你使用FragmentTraction做出一些事務,你必須調用commit()函數來使它生效.
Adding a fragment to an activity
上面的例子展示了怎麼增加一個用來展示UI的Fragment到你的Activity中.你也能使用Fragment做一些後臺操作,而不用顯示不必要的UI.
想要增加一個沒有UI的Fragment,在Activity中使用add(Fragment,string)方法增加這個Fragment(提供一個”tag”字符串,而不是資源ID).但是因爲它沒有和Activity中的某個View關聯,它不會接收oncreateView的返回值.所以你不必實現這個方法.
提供tag字符串並不是只有不含UI的Fragment纔可以,你也能給一個有UI的Fragment提供tag.但是如果這個Fragment沒有UI,那麼這個tag就是它的唯一標識.
你稍後想找到這個Fragment的話,你需要去使用findFragmentByTag()方法.
在FragmentRetainInstance.java
文件裏有一個沒有UI的Fragment的例子.這個文件在SDKmanager裏面可以下載到,下載後的文件路徑是<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java
.
想在你的Activity裏面管理Fragments,你需要使用FragmentManager類.使用Activity的getFragmentManager()方法可以得到它.
使用FragmentManager可以做到的事情包括:
1.使用findFragmentById()方法得到一個Activity中已經存在的Fragment(提供了UI).
或者findFragmentByTag()(沒有UI).
2.從回退棧中將Fragment彈出,使用popBackStack()方法.(模擬一個用戶返回的操作)
3.使用addOnBackStackChangeListener()給回退棧註冊一個監聽器.
更多的其他方法,請查閱FragmentManager的API文檔.
還有在前一個部分提到過的,你也能用FragmentManager類去開始一個Fragmenttransaction.以便於你執行一個增加或者移除的事務.
Performing Fragment Transactions
在Activity中使用Fragments的一大特點就是你可以增加,刪除,替換以及對他們進行一些其他操作,以響應用戶交互.你提交給Activity的每一套操作都被稱爲事務.你可以使用Fragmenttransaction的API執行一些操作.你也能保存每個事務到被Activity管理的回退棧中,使得用戶可以導航回退一些Fragment的操作.
你能像這樣得到Fragmentmanager的實例:
FragmentManager FragmentManager = getFragmentManager();
FragmentTransaction FragmentTransaction = FragmentManager.beginTransaction();
每個事務是你想執行的一連串的操作.你可以準備所有你會使用諸如add(),remove()和replace()執行的事務.然後調用commit()方法,將事務提交給Activity去執行.
調用commit()方法之前,你也可能想去調用addtoBackstack()方法把你的事務加入到一個由Activity管理的Fragment的事務的回退棧中.這個由Activity管理的回退棧允許用戶通過實體返回鍵返回上一個Fragment.
下面這個例子演示了怎麼將你的Fragment換成另一個,並且在回退棧中保存之前的狀態.
// Create new Fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the Fragment_container view with this Fragment,
// and add the transaction to the back stack
transaction.replace(R.id.Fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
上例中, newFragment 替換了當前佈局中用 R.id.Fragment_container這個ID標識的Fragment.
通過調用addtobackstack(),這個替換的事務被回退棧保存,所以用戶能夠用返回鍵返回被替換之前的fragment.
如果做出了多個改變在一個事務中(比如同時調了add()和remove())然後調用addtobackstack().那麼在你調用commit()方法之前所做的所有改變都會被當成一個整體存到回退棧中.這時候返回鍵會一次性返回到所有這些改變之前的狀態.
你把操作加入Fragmenttransaction的順序無所謂,但是你要注意:
1. 你必須調用commit()方法.
2. 如果你在一個容器中加入了多個Fragment,你加入它們的順序決定了它們的顯示層次.
如果你沒有調用addtobackstack()方法那麼當你執行移除Fragment的操作時,你commit之後,這個Fragment就會被銷燬,不能夠再返回這個Fragment.然而如果你調用了,那麼它就會進入stop狀態,並且用戶返回時將會恢復.
提示:如果你想加上Fragment切換動畫,commit之前調用settransiton()方法.
調用commit()方法並不會立即執行你的事務.它被安排你的UI線程有空閒執行的時候去執行.如果必要的話,我們也提供了executePendingTransactions() 方法讓你的UI線程能立即去執行commit()的事務.除非這個事務依賴於其他線程的一些工作,否則這麼做沒有太大必要.(譯者注:因爲延遲很短,可以忽略不計)
注意:你只能在Activity”保存狀態”(用戶離開Activity)之前提交你的事務.否則將會拋出異常.這是因爲如果Activity需要被銷燬的話,Activity的狀態可能會丟失.
如果即使丟失了狀態也要執行,你可以使用commitAllowingStateLoss().
Communicating with the Activity
儘管Fragment是作爲一個獨立於Activity實現的,但是作爲一個給定的Fragment的實例還是被直接綁在一個容納它的Activity上.
具體的來說,Fragment內部能用getActivity()方法獲得Activity實例,並且可以非常容易的在Activity中做一些例如findview的操作.
<pre name="code" class="java">View listView = getActivity().findViewById(R.id.list);
相同的,你的Activity能通過某個Fragment的實例調用Fragment的方法.從Fragmentmanager中的findFragmentById() 或 findFragmentByTag()方法你能獲得Fragment的引用.
ExampleFragment Fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_Fragment);
Creatingevent callbacks to the Activity
在一些情況下,你可能需要Fragment和Activity共享事件.這樣做的一個好辦法就是在Fragment中定義一個回調接口,讓Activity去實現它.當Activity從回調接口中收到事件通知時,它就可以把這些信息和其他Fragment分享,如果有必要的話.
例如,一個新聞app的某個Activity中有兩個Fragment----一個用來展示新聞列表(Fragment A),一個用來展示新聞內容(Fragment B).那麼Fragment A必須告訴Activity某個新聞項目被點擊了,Activity再去通知Fragment B,讓Fragment B去展示這篇新聞.
下面這個例子中, OnArticleSelectedListener
接口定義在Fragment
A中:
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
然後Activity去實現這個監聽接口,並且複寫了
onArticleSelected()
方法去告知Fragment
B一些FragmentA 中發生的事件.爲了保證宿主Activity實現了這個接口.Fragment A的onattach()回調方法(系統把Fragment增加到Activity的時候回調此方法)強轉了傳入onattach中的Activity實例.並用這個Activity實例去初始化了 OnArticleSelectedListener
接口的一個實例.
<span style="font-size:18px;">public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity Activity) {
super.onAttach(Activity);
try {
mListener = (OnArticleSelectedListener) Activity;
} catch (ClassCastException e) {
throw new ClassCastException(Activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}</span>
如果這個Activity沒有實現接口,那麼Fragment就會拋出強轉異常.如果成功的話,mListener成員變量就持有了一個實現了回調接口的Activity的引用.這樣FragmentA就可以通過調用接口中的方法和Activity共享一些事件了.例如,如果Fragment A是一個列表Fragment.它就可以在每次列表被點擊的時候調用 OnArticleSelectedListener
接口中定義的onArticleSelected()
方法去通知Activity.
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host Activity
mListener.onArticleSelected(noteUri);
}
...
}
Addingitems to the Action Bar
你的Fragment能夠通過實現 onCreateOptionsMenu()方法給Activity的選項菜單增加菜單項(同樣也包括ActionBar)
爲了使你實現的這個方法被調用,你必須在onCreate()的時候調用setHasOptionsMenu()方法來告訴系統這個Fragment要給菜單里加東西,否則你的onCreateOptionsMenu()方法不會被調用.
你的Fragment往菜單裏添加的任何條目都會添加到現存的菜單列表裏.菜單項被選擇的時候,Fragment的onOptionsItemSelected()方法也會被回調.
通過調用registerForContextMenu()方法,你也依然能在你的Fragment佈局中註冊一個View以提供一個ContextMenu.當用戶打開ContextMenu,你的onCreateContextMenu()方法將被回調.當用戶點擊ContextMenu的菜單項,Fragment的onContextItemSelected()方法將被回調.
注意:儘管你的Fragment收到來自它自己添加的菜單項的on-item-selected回調,當用戶點擊時,它的Activity仍然是首先收到它自己的回調函數.如果Activity沒有處理此事件,那麼事件纔會被傳遞到Fragment的回調.對於Options Menu和context menus都是這樣
關於菜單的更多信息,請查看菜單和actionbar的開發指南.
Handling the Fragment Lifecycle
Fragment的生命週期和Activity的很像.就像Activity一樣,一個Fragment能存在三種狀態.
Resumed
Fragment在一個運行的Activity中可見
Paused
另一個Activity在前臺並且獲得了焦點,但是Fragment所在的Activity仍然可見.(前臺的Activity部分透明或者沒有覆蓋整個屏幕)
Stopped
Fragment不可見,原因有:
1. 宿主Activity也被停止了.
2. Fragment被移除但是加入了回退棧
一個停止的Fragment仍然存活,系統保留了它的所有狀態和成員信息.但是它已經不再可見並且如果Activity被銷燬,它也會被銷燬.
同樣的,你能用bundle去保存Fragment的狀態.如果那樣的話,你能保存狀態當Fragment的onSaveInstanceState()回調的時候,並且在onCreate(), onCreateView(), oronActivityCreated()的時候恢復他們.想知道更過關於保存狀態的信息,請查看Activity的文檔.
Activity和Fragment生命週期最大的不同就是他們是如何被存儲進各自的回退棧中的.當Activity進入停止狀態時,它會默認被加入一個由系統管理的回退棧中(所以用戶才能用返回鍵返回上一個Activity).而相關的Fragment想加入回退棧中只有當你移除它時顯式的調用addToBackStack()方法.
除了這個,管理Fragment的生命週期非常像管理Activity的生命週期.你需要額外注意的就是Activity的生命週期是如何影響Fragment的.
注意:如果你在Fragment裏需要獲得context對象,你可以調用getActivity()方法.但是需要小心的去使用這個方法.只有你的Fragment被附到Activity上的時候,你才能得到正確的結果.當你的Fragment還沒有附上的時候,或在生命週期結束時已經被從Activity剝離了,那麼getActivity()將返回null.
Coordinatingwith the Activity lifecycle
Activity的生命週期會直接影響Fragment的生命週期,幾乎每個Activity生命週期回調方法Fragment都會有一個與之類似的返回.例如,當Activity收到onPause()回調是,其中的每個Fragment都會收到onPause()回調.
Fragment有很少的額外生命週期回調.即便這樣,這幾個額外的回調還是處理了許多和Activity的特殊相互作用,例如創建或銷燬Fragment的UI.這些額外的方法就是:
onAttach()
當Fragment和Activity發生聯繫的時候調用(Activity會被傳進來)
onCreateView()
調用這個方法去建立和Fragment相關聯的視圖層
onActivityCreated()
當Activity的oncreate方法返回時
onDestroyView()
當Fragment相應的UI被銷燬時
onDetach()
當Fragment和Activity斷開聯繫時調用
Fragment的生命流程圖,以及它是如何被其宿主Activity所影響的,已經展示在圖3中.在這張圖中你能看到一個連續的Activity的狀態是如何決定每個Fragment的回調函數何時調用的.例如,當Activity收到他的onCreate()回調,它當中的Fragment收到不僅僅是onActivityCreated()回調
一旦Activity進入Resume的狀態,你能自由的增加和移除Fragments到Activity中.因此,只有當Activity進入Resume狀態的時候,Fragment的生命週期才能獨立改變.
即便如此,當Activity離開恢復狀態的時候,Fragment纔會再一次被Activity推動它的生命週期歷程.
Example
爲了兼顧上述文檔中提到的所有內容,這裏是一個單Activity使用雙Fragment來創建一個雙面板佈局的例子.下面將要展示的這個Activity包含一個用來展示莎士比亞戲劇的標題的Fragment和一個用來展示從標題欄選擇之後的喜劇內容的Fragment.它也提供了Fragment和Activity如何適應橫豎屏的解決方案.
加載MainActivity的代碼
<span style="font-size:18px;">@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.Fragment_layout);
}</span>
佈局文件 Fragment_layout.xml
如下
<span style="font-size:18px;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<Fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent" />
<FrameLayout android:id="@+id/details" android:layout_weight="1"
android:layout_width="0px" android:layout_height="match_parent"
android:background="?android:attr/detailsElementBackground" />
</LinearLayout></span>
使用這個佈局,系統會在Activity加載佈局的同時初始化TitlesFragment,與此同時,一個framlayout佔據了屏幕右邊的空間,但是什麼都沒有.正如你將要看見的,直到用戶從選擇了某個選項之後,一個Fragment才被插入到這個FrameLayout中.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles"
android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>
但是並不是所有的屏幕都寬到能並排顯示這兩個Fragment.所以上面這個佈局res/layout-land/Fragment_layout.xml僅僅適用於橫屏的時候.
因此當屏幕是豎屏的時候,系統就會使用保存在res/layout/Fragment_layout.xml
:的佈局.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="@+id/titles"
android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>
這個佈局僅僅包含 TitlesFragment
.這意味着,當設備在豎屏的時候,只有戲劇的列表是可見的.所以,當用戶在豎屏下點擊某個列表項的時候.app就會開啓一個新的Activity來展示戲劇內容,而不是加載第二個Fragment.
下一步示範了Fragment類的實現.首先是TitlesFragment
類,展示了莎士比亞的歌劇標題.這個Fragment是 ListFragment的子類.它處理了列表的工作.
當你仔細閱讀以下代碼的時候,你會注意到當用戶點擊列表項時,可能有兩種情況,這取決於這兩個佈局中哪個是激活狀態,它有可能會增加一個新佈局,或者開啓一個新的Activity.
public static class TitlesFragment extends ListFragment {
boolean mDualPane;
int mCurCheckPosition = 0;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Populate list with our static array of titles.
setListAdapter(new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));
// Check to see if we have a frame in which to embed the details
// Fragment directly in the containing UI.
View detailsFrame = getActivity().findViewById(R.id.details);
mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
if (mDualPane) {
// In dual-pane mode, the list view highlights the selected item.
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// Make sure our UI is in the correct state.
showDetails(mCurCheckPosition);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
showDetails(position);
}
/**
* Helper function to show the details of a selected item, either by
* displaying a Fragment in-place in the current UI, or starting a
* whole new Activity in which it is displayed.
*/
void showDetails(int index) {
mCurCheckPosition = index;
if (mDualPane) {
// We can display everything in-place with Fragments, so update
// the list to highlight the selected item and show the data.
getListView().setItemChecked(index, true);
// Check what Fragment is currently shown, replace if needed.
DetailsFragment details = (DetailsFragment)
getFragmentManager().findFragmentById(R.id.details);
if (details == null || details.getShownIndex() != index) {
// Make new Fragment to show this selection.
details = DetailsFragment.newInstance(index);
// Execute a transaction, replacing any existing Fragment
// with this one inside the frame.
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (index == 0) {
ft.replace(R.id.details, details);
} else {
ft.replace(R.id.a_item, details);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
} else {
// Otherwise we need to launch a new Activity to display
// the dialog Fragment with selected text.
Intent intent = new Intent();
intent.setClass(getActivity(), DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
}
第二個Fragment展示了從TitlesFragment
中選擇的戲劇的內容.
public static class DetailsFragment extends Fragment {
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
public static DetailsFragment newInstance(int index) {
DetailsFragment f = new DetailsFragment();
// Supply index input as an argument.
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
public int getShownIndex() {
return getArguments().getInt("index", 0);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// Fragment's containing frame doesn't exist. The Fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
ScrollView scroller = new ScrollView(getActivity());
TextView text = new TextView(getActivity());
int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
4, getActivity().getResources().getDisplayMetrics());
text.setPadding(padding, padding, padding, padding);
scroller.addView(text);
text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
return scroller;
}
}
回顧 TitlesFragment
類,如果用戶點擊了某個列表項並且當前佈局中不包含R.id.details
view(包含DetailsFragment
的佈局),那麼Activity就會開啓DetailsActivity
Activity類來展示列表項的內容.
下面是在豎屏上用來替代DetailsFragment
的 DetailsActivity
類.
public static class DetailsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// If the screen is now in landscape mode, we can show the
// dialog in-line with the list so we don't need this Activity.
finish();
return;
}
if (savedInstanceState == null) {
// During initial setup, plug in the details Fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
}
}
}
需要注意的是如果配置爲橫屏,那麼這個Activity就會結束它自己讓main Activity接管,然後在旁邊展示.這會發生在如果用戶開啓Activity的時候是豎屏,但是之後旋轉爲橫屏.