最值得看的android系統fragment教程--翻譯自安卓官方文檔

注意:如果你是用的是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.


Managing Fragments

想在你的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 Menucontext 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.detailsview(包含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的時候是豎屏,但是之後旋轉爲橫屏.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章