Fragment 概要
Fragment表示 Activity 中的行為或用戶界面部分。您可以將多個片段(片段就是指 Fragment )組合在一個 Activity 中來構(gòu)建多窗格 UI,以及在多個 Activity 中重復使用某個片段。您可以將片段視為 Activity 的模塊化組成部分,它具有自己的生命周期,能接收自己的輸入事件,并且您可以在 Activity 運行時添加或移除片段(有點像您可以在不同 Activity 中重復使用的“子 Activity”)。
片段必須始終嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影響。 例如,當 Activity 暫停時,其中的所有片段也會暫停;當 Activity 被銷毀時,所有片段也會被銷毀。
當您將片段作為 Activity 布局的一部分添加時,它存在于 Activity 視圖層次結(jié)構(gòu)的某個 ViewGroup 內(nèi)部,并且片段會定義其自己的視圖布局。您可以通過在 Activity 的布局文件中聲明片段,將其作為 <fragment> 元素插入您的 Activity 布局中,即靜態(tài)添加?;蛘咄ㄟ^將其添加到某個現(xiàn)有 ViewGroup,利用應(yīng)用代碼進行動態(tài)插入。不過,片段并非必須成為 Activity 布局的一部分;您還可以將沒有自己 UI 的片段用作 Activity 的不可見工作線程。
Fragment 作用
Android 在 Android 3.0 (API 11)中引入了片段,主要是為了給更大的屏幕(如平板電腦)上更加動態(tài)和靈活的 UI 設(shè)計提供支持。由于平板屏幕比手機的屏幕大得多,也能顯示更多的布局和組件。
例如,新聞應(yīng)用可以使用一個片段在左側(cè)顯示文章列表,使用另一個片段在右側(cè)顯示文章 — 兩個片段并排顯示在一個 Activity 中,每個片段都具有自己的一套生命周期回調(diào)方法,并各自處理自己的用戶輸入事件。 因此,用戶不需要使用一個 Activity 來選擇文章,然后使用另一個 Activity 來閱讀文章,而是可以在同一個 Activity 內(nèi)選擇文章并進行閱讀,如圖 1 中的平板電腦布局所示。

在平板電腦尺寸的設(shè)備上運行時,該應(yīng)用可以在 Activity A 中嵌入兩個片段。 不過,在手機尺寸的屏幕上,沒有足以儲存兩個片段的空間,因此Activity A 只包括用于顯示文章列表的片段,當用戶選擇文章時,它會啟動Activity B,其中包括用于閱讀文章的第二個片段。 因此,應(yīng)用可通過重復使用不同組合的片段來同時支持平板電腦和手機,如圖 1 所示。
Fragment 生命周期

可以看到 Fragment 的生命周期和 Activity 很相似,只是多了一下幾個方法:
onAttach() 在Fragment 和 Activity 建立關(guān)聯(lián)是調(diào)用(Activity 傳遞到此方法內(nèi))
onCreateView() 當Fragment 創(chuàng)建視圖時調(diào)用
onActivityCreated() 在相關(guān)聯(lián)的 Activity 的 onCreate() 方法已返回時調(diào)用。
onDestroyView() 當Fragment中的視圖被移除時調(diào)用
onDetach() 當Fragment 和 Activity 取消關(guān)聯(lián)時調(diào)用。
可以看下幾種操作情況下Fragment 的生命周期變化

管理 Fragment 生命周期和 Activity 生命周期很相似,同時 Activity 的生命周期對 Fragment 的生命周期也有一定的影響,如下圖所示

用下圖(來源)來表示 Activity 和 Fragment 的生命周期變化的先后過程是:

Fragment 生命周期與 Activity 生命周期的一個關(guān)鍵區(qū)別就在于,F(xiàn)ragment 的生命周期方法是由托管Activity而不是操作系統(tǒng)調(diào)用的。Activity 中生命周期方法都是 protected,而 Fragment 都是 public,也能印證了這一點,因為 Activity 需要調(diào)用 Fragment 那些方法并管理它。
加載 Fragment
- 靜態(tài)加載
- 動態(tài)加載
- 靜態(tài)加載 在 Activity 的布局文件內(nèi)聲明片段
下邊代碼是含有兩個 Fragment 的 Activity 布局
<?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 對應(yīng)的具體包名路徑,當系統(tǒng)創(chuàng)建此 Activity 布局時,會實例化在布局中指定的每個 fragment,并為每個 fragment 調(diào)用 onCreateView()方法,以檢索每個 fragment 的布局。系統(tǒng)會直接插入 fragment 返回的 View 來替代 fragment 元素。
并且在 Activity 活動里可以直接使用 findViewById() 方法獲取 fragment 對應(yīng)布局里的控件。同樣在 fragment 里可以直接使用 getActivity()方法獲得綁定的主 Activity 實例,并調(diào)用 Activity 里的方法或其他 fragment 實例。
- 動態(tài)加載 通過編程方式將 fragment 添加到某個activity布局里現(xiàn)有的 ViewGroup (例如 LinearLayout 或 FrameLayout)里。
要想在 Avtivity 中執(zhí)行 Fragment 事務(wù) (如添加、刪除或替換 Fragment),必須使用 FragmentTransaction 中的 API??梢允褂孟旅孢@樣從 Activity 中獲取一個 FragmentTransaction。
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后可以使用 add()方法添加一個 fragment ,指定要添加的 fragment 和插入到哪個視圖。例如
ExampleFragment exampleFragment = new ExampleFragment();
fragmentTransaction.add(R.id.frame_layout,exampleFragment);
fragmentTransaction.commit();
add 方法中第一個參數(shù)是一個activity 對應(yīng)布局文件中的 ViewGroup,即應(yīng)該放置 fragment 的位置,由資源 ID 指定,第二個參數(shù)是加入的 fragment ,一旦通過 fragmentTransaction 做了更改,最后必須使用 commit 方法以使更改生效。
添加沒有 UI 的 fragment
上邊展示了如何向您的 Activity 添加 fragment 以提供 UI。不過,還可以使用 fragment 為 Activity 提供后臺行為,而不顯示額外 UI。
要想添加沒有 UI 的片段,請使用 add(Fragment, String) 從 Activity 添加片段(為片段提供一個唯一的字符串“標記”,而不是視圖 ID)。 這會添加片段,但由于它并不與 Activity 布局中的視圖關(guān)聯(lián),因此不會收到對onCreateView() 的調(diào)用。因此,不需要實現(xiàn)該方法。
執(zhí)行 Fragment 事務(wù)
在 Activity 中使用 Fragment 可以很方便的進行添加 add、替換 replace、移除 remove 等操作,這樣提交給 Activity 的每組更改都可以稱為事務(wù)。像上邊動態(tài)添加 fragment 那樣,使用 FragmentTransaction 里的 API 就可以執(zhí)行一項事務(wù)。同時也可以將此事務(wù)保存到 Activity 管理的返回棧中,從而用戶可以回退到 fragment 改變之前的狀態(tài)(類似于 activity 回退到上一個頁面)。
下邊的例子演示了如何替換一個 fragment,并把替換之前的狀態(tài)保存到 Activity 的返回棧中。
Fragment newFragment = new ExampleFragment();
FragmentTransaction mTransaction = getFragmentManager().beginTransaction();
//用新的 fragment 替換原來fragment 所在位置的布局,并且把此事務(wù)添加到返回棧中。
mTransaction.replace(R.id.frame_layout,newFragment);
mTransaction.addToBackStack(null);
mTransaction.commit();
如果在執(zhí)行移除 fragment 的事務(wù)時沒有調(diào)用 addToBackStack(),則事務(wù)提交時該 fragment 會被銷毀,用戶將無法回退到該 fragment 。 不過,如果在刪除 fragment 時調(diào)用了 addToBackStack(),則系統(tǒng)會停止該 fragment,并在用戶按返回鍵時將其恢復。
Fragment 與 Activity 通信
上邊說過,在 fragment 中可以調(diào)用 getActivity() 獲取 activity 的實例并調(diào)用 activity 里的方法和布局,同樣在 activity 里也可以通過 findFragmentById()(對于在 activity 提供 fragment 布局的) 或 findFragmentByTag() (對于在 activity 提供或者不提供 fragment 布局的)方法獲取 fragment 的實例,例如在 activity 中從 FragmentManager 獲取對 Fragment 的引用來調(diào)用 fragment 中的方法:
Fragment fragment = getFragmentManager.findFragmentById(R.id.fragment_container);
使用 FragmentManager 還可以執(zhí)行的操作包括:
- 通過 findFragmentById 或 findFragmentByTag 獲取 activity 中存在的 fragment 的實例
- 通過 popBackStack (模擬用戶點擊返回按鈕操作)將 fragment 從返回棧中彈出
- 通過 addOnBackStackChangedListener() 注冊一個監(jiān)聽返回棧改變的監(jiān)聽器
- 像上邊生成 fragmentTransaction 的方法,可以使用 fragmentManager 生成一個 fragmentTransaction 來執(zhí)行某些事務(wù),比如添加、替換、移除、addToBackStack()等。