Android魔幻之旅(四):管理Fragment

本文內(nèi)容:
  • 設(shè)計(jì)哲學(xué)
  • 創(chuàng)建fragment
  • 添加用戶界面
  • 向Activity添加fragment
  • 管理fragment
  • 執(zhí)行fragment事務(wù)
    • FragmentTransaction中的常用方法
    • Fragment的靜態(tài)加載
  • 與Activity通信
    • 創(chuàng)建對(duì)Activity的事件回調(diào)
    • 向App Bar添加菜單
  • 處理fragment生命周期
    • 與 Activity 生命周期協(xié)調(diào)一致

Fragment 表示 Activity 中的一個(gè)行為或UI的一部分。您可以將多個(gè)fragment組合在一個(gè) Activity 中來構(gòu)建多窗格 UI沙合,以及在多個(gè) Activity 中重復(fù)使用某個(gè)Fragment。您可以將片段視為 Activity 的模塊化組成部分,它具有自己的生命周期昭躺,能接收自己的輸入事件缀台,并且在 Activity 運(yùn)行時(shí)可以被添加或移除(有點(diǎn)像您可以在不同 Activity 中重復(fù)使用的“子 Activity”)岳掐。

Fragment必須始終嵌入在 Activity 中凭疮,其生命周期直接受宿主 Activity 生命周期的影響。 例如岩四,當(dāng) Activity 暫停時(shí)哭尝,其中的所有Fragment也會(huì)暫停;當(dāng) Activity 被銷毀時(shí)剖煌,所有Fragment也會(huì)被銷毀。 不過逝淹,當(dāng) Activity 正在運(yùn)行時(shí)(處于onResume的生命周期狀態(tài))耕姊,您可以獨(dú)立操縱每個(gè)Fragment,如添加或移除它們栅葡。 當(dāng)您執(zhí)行此類fragment事務(wù)時(shí)茉兰,您也可以將fragment添加到由 Activity 管理的返回棧——Activity后退棧中的每一條記錄都是一條已發(fā)生的fragment事務(wù)欣簇。 返回棧讓用戶可以通過按返回按鈕撤消Fragment事務(wù)(后退)规脸。

當(dāng)您將fragment作為 Activity 布局的一部分添加時(shí),它存在于 Activity 視圖層次結(jié)構(gòu)的某個(gè) ViewGroup 內(nèi)部熊咽,并且Fragment會(huì)定義其自己的視圖布局莫鸭。您可以通過在 Activity 的布局文件中聲明Fragment,將其作為 <fragment> 元素插入您的 Activity 布局中横殴,或者通過將其添加到某個(gè)現(xiàn)有 ViewGroup被因,利用應(yīng)用代碼進(jìn)行插入。不過衫仑,fragment并非必須成為 Activity 布局的一部分梨与;您還可以將沒有自己 UI 的fragment用作 Activity 的不可見工作線程。

本文描述如何在開發(fā)您的應(yīng)用時(shí)使用fragment文狱,包括將fragment添加到 Activity 返回棧時(shí)如何保持其狀態(tài)粥鞋、如何與 Activity 及 Activity 中的其他fragment共享事件、如何為 Activity 的操作欄發(fā)揮作用等等瞄崇。

設(shè)計(jì)哲學(xué)


Android 在 Android 3.0(API 級(jí)別 11)中引入了fragment呻粹,主要是為了給大屏幕(如平板電腦)上更加動(dòng)態(tài)和靈活的 UI 設(shè)計(jì)提供支持到踏。由于平板電腦的屏幕比手機(jī)屏幕大得多,因此可用于組合和交換 UI 組件的空間更大尚猿。利用fragment實(shí)現(xiàn)此類設(shè)計(jì)時(shí)窝稿,您無需管理對(duì)視圖層次結(jié)構(gòu)的復(fù)雜更改。 通過將 Activity 布局分成fragment凿掂,您可以在運(yùn)行時(shí)修改 Activity 的外觀伴榔,并在由 Activity 管理的返回棧中保留這些更改。

例如庄萎,新聞應(yīng)用可以使用一個(gè)fragment在左側(cè)顯示文章列表踪少,使用另一個(gè)fragment在右側(cè)顯示文章—— 兩個(gè)fragment并排顯示在一個(gè) Activity 中,每個(gè)fragment都具有自己的一套生命周期回調(diào)方法糠涛,并各自處理自己的用戶輸入事件援奢。 因此,用戶不需要使用一個(gè) Activity 來選擇文章忍捡,然后使用另一個(gè) Activity 來閱讀文章集漾,而是可以在同一個(gè) Activity 內(nèi)選擇文章并進(jìn)行閱讀,如圖 1 中的平板電腦布局所示砸脊。

圖 1

您應(yīng)該將每個(gè)fragment都設(shè)計(jì)為可重復(fù)使用的模塊化 Activity 組件具篇。也就是說,由于每個(gè)fragment都會(huì)通過各自的生命周期回調(diào)來定義其自己的布局和行為凌埂,您可以將一個(gè)fragment加入多個(gè) Activity驱显,因此,您應(yīng)該采用可復(fù)用式設(shè)計(jì)瞳抓,避免直接從某個(gè)fragment直接操縱另一個(gè)fragment埃疫。 這特別重要,因?yàn)槟K化fragment讓您可以通過更改fragment的組合方式來適應(yīng)不同的屏幕尺寸孩哑。 在設(shè)計(jì)可同時(shí)支持平板電腦和手機(jī)的應(yīng)用時(shí)栓霜,您可以在不同的布局配置中重復(fù)使用fragment,以根據(jù)可用的屏幕空間優(yōu)化用戶體驗(yàn)臭笆。 例如叙淌,在手機(jī)上,如果不能在同一 Activity 內(nèi)儲(chǔ)存多個(gè) fragment 愁铺,可能必須利用單獨(dú)fragment來實(shí)現(xiàn)單窗格 UI鹰霍。

創(chuàng)建fragment


要想創(chuàng)建fragment,您必須創(chuàng)建 Fragment 的子類(或已有的Fragment子類)茵乱。Fragment 類的代碼與 Activity 非常相似茂洒。它包含與 Activity 類似的回調(diào)方法,如 onCreate()瓶竭、onStart()督勺、onPause() 和 onStop()渠羞。實(shí)際上,如果您要將現(xiàn)有 Android 應(yīng)用轉(zhuǎn)換為使用fragment智哀,可能只需將代碼從 Activity 的回調(diào)方法移入fragment相應(yīng)的回調(diào)方法中次询。

通常,您至少應(yīng)實(shí)現(xiàn)以下生命周期方法:

onCreate()
系統(tǒng)會(huì)在創(chuàng)建 fragment 時(shí)調(diào)用此方法瓷叫。在實(shí)現(xiàn)內(nèi)應(yīng)該初始化想在ragment暫屯偷酰或停止后恢復(fù)時(shí)保留的必需fragment組件。

onCreateView()
系統(tǒng)會(huì)在fragment首次繪制其UI時(shí)調(diào)用此方法摹菠。 要想為您的fragment繪制 UI盒卸,您從此方法中返回的 View 必須是fragment布局的根視圖。如果fragment未提供 UI次氨,您可以返回 null蔽介。

onPause()
系統(tǒng)將此方法作為用戶離開fragment的第一個(gè)信號(hào)(但并不總是意味著此fragment會(huì)被銷毀)進(jìn)行調(diào)用。 您通常應(yīng)該在此方法內(nèi)確認(rèn)在當(dāng)前用戶會(huì)話結(jié)束后仍然有效的任何更改(因?yàn)橛脩艨赡懿粫?huì)返回)煮寡。

大多數(shù)應(yīng)用都應(yīng)該至少為每個(gè)fragment實(shí)現(xiàn)這三個(gè)方法虹蓄,但您還應(yīng)該使用幾種其他回調(diào)方法來處理fragment生命周期的各個(gè)階段。

圖 2

您可能還想擴(kuò)展幾個(gè)子類洲押,而不是 Fragment 基類:

DialogFragment
顯示浮動(dòng)對(duì)話框武花。使用此類創(chuàng)建對(duì)話框可有效地替代使用 Activity 類中的對(duì)話框幫助程序方法,因?yàn)槟梢詫⑵螌?duì)話框納入由 Activity 管理的片段返回棧杈帐,從而使用戶能夠返回清除的片段。

ListFragment
顯示由適配器(如 SimpleCursorAdapter)管理的一系列項(xiàng)目专钉,類似于 ListActivity挑童。它提供了幾種管理列表視圖的方法,如用于處理點(diǎn)擊事件的 onListItemClick() 回調(diào)跃须。

PreferenceFragment
以列表形式顯示 Preference 對(duì)象的層次結(jié)構(gòu)站叼,類似于 PreferenceActivity。這在為您的應(yīng)用創(chuàng)建“設(shè)置” Activity 時(shí)很有用處菇民。

添加用戶界面

Fragment 通常用作 Activity UI的一部分尽楔,將其自己的布局融入 Activity第练。要想為fragment提供布局阔馋,您必須實(shí)現(xiàn) onCreateView() 回調(diào)方法,Android 系統(tǒng)會(huì)在需要繪制其布局時(shí)調(diào)用該方法娇掏。您對(duì)此方法的實(shí)現(xiàn)返回的 View 必須是片段布局的根視圖呕寝。

:如果fragment是 ListFragment 的子類,則默認(rèn)實(shí)現(xiàn)會(huì)從onCreateView()返回一個(gè) ListView婴梧,因此無需實(shí)現(xiàn)它下梢。

要想從 onCreateView() 返回布局客蹋,您可以通過 XML 中定義的布局資源來擴(kuò)展布局。為幫助您執(zhí)行此操作孽江,onCreateView() 提供了一個(gè) LayoutInflater 對(duì)象讶坯。

例如,以下這個(gè) 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 參數(shù)是fragment布局將插入到的父 ViewGroup(來自 Activity 的布局)岗屏。savedInstanceState 參數(shù)是在恢復(fù)片段時(shí)辆琅,提供上一片段實(shí)例相關(guān)數(shù)據(jù)的 Bundle。

inflate() 方法帶有三個(gè)參數(shù):

  • 您想要擴(kuò)展的布局的資源 ID担汤;
  • 將作為擴(kuò)展布局父項(xiàng)的 ViewGroup涎跨。傳遞 container 對(duì)系統(tǒng)向擴(kuò)展布局的根視圖(由其所屬的父視圖指定)應(yīng)用布局參數(shù)具有重要意義;
  • 指示是否應(yīng)該在擴(kuò)展期間將擴(kuò)展布局附加至 ViewGroup(第二個(gè)參數(shù))的布爾值崭歧。(在本例中隅很,其值為 false,因?yàn)橄到y(tǒng)已經(jīng)將擴(kuò)展布局插入 container —— 傳遞 true 值會(huì)在最終布局中創(chuàng)建一個(gè)多余的視圖組率碾。)
    現(xiàn)在叔营,您已經(jīng)了解了如何創(chuàng)建提供布局的fragment。接下來所宰,您需要將該fragment添加到您的 Activity 中绒尊。

向Activity添加fragment

通常,fragment向宿主 Activity 貢獻(xiàn)一部分 UI仔粥,作為 Activity 總體視圖層次結(jié)構(gòu)的一部分嵌入到 Activity 中婴谱。可以通過兩種方式向 Activity 布局添加fragment:

  • 在 Activity 的布局文件內(nèi)聲明fragment

在本例中躯泰,您可以將fragment當(dāng)作視圖來為其指定布局屬性谭羔。 例如,以下是一個(gè)具有兩個(gè)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 屬性指定要在布局中實(shí)例化的 Fragment 類麦向。

  • 或者通過編程方式將 fragment 添加到某個(gè)現(xiàn)有 ViewGroup

您可以在 Activity 運(yùn)行期間隨時(shí)將 fragment 添加到 Activity 布局中瘟裸。您只需指定要將 fragment 放入哪個(gè) ViewGroup。

要想在您的 Activity 中執(zhí)行 fragment (如添加诵竭、移除或替換 fragment )话告,您必須使用 FragmentTransaction 中的 API。您可以像下面這樣從 Activity 獲取一個(gè) FragmentTransaction 實(shí)例:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

然后卵慰,您可以使用 add() 方法添加一個(gè) fragment 沙郭,指定要添加的 fragment 以及將其插入哪個(gè)視圖。例如:

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

傳遞到 add() 的第一個(gè)參數(shù)是 ViewGroup呵燕,即應(yīng)該放置 fragment 的位置棠绘,由資源 ID 指定,第二個(gè)參數(shù)是要添加的 fragment 。
一旦您通過 FragmentTransaction 做出了更改氧苍,就必須調(diào)用 commit() 以使更改生效夜矗。

添加沒有 UI 的 fragment

例展示了如何向您的 Activity 添加 fragment 以提供 UI。不過让虐,您還可以使用 fragment 為 Activity 提供后臺(tái)行為紊撕,而不顯示額外 UI。

要想添加沒有 UI 的 fragment 赡突,請(qǐng)使用 add(Fragment, String) 從 Activity 添加 fragment (為 fragment 提供一個(gè)唯一的字符串“標(biāo)記”对扶,而不是視圖 ID)。 這會(huì)添加 fragment 惭缰,但由于它并不與 Activity 布局中的視圖關(guān)聯(lián)浪南,因此不會(huì)收到對(duì) onCreateView() 的調(diào)用。因此漱受,您不需要實(shí)現(xiàn)該方法络凿。

并非只能為非 UI 片段提供字符串標(biāo)記 — 您也可以為具有 UI 的片段提供字符串標(biāo)記 — 但如果片段沒有 UI,則字符串標(biāo)記將是標(biāo)識(shí)它的唯一方式昂羡。如果您想稍后從 Activity 中獲取fragment絮记,則需要使用 findFragmentByTag()。

如需查看將沒有 UI 的fragment用作后臺(tái)工作線程的示例 Activity虐先,請(qǐng)參閱 FragmentRetainInstance.java 示例怨愤,該示例包括在 SDK 示例(通過 Android SDK 管理器提供)中,以
<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java 形式位于您的系統(tǒng)中蛹批。

管理fragment


要想管理您的 Activity 中的 fragment 撰洗,您需要使用 FragmentManager。要想獲取它腐芍,請(qǐng)從您的 Activity 調(diào)用 getFragmentManager()了赵。

您可以使用 FragmentManager 執(zhí)行的操作包括:

  • 通過 findFragmentById()(對(duì)于在 Activity 布局中提供 UI 的片段)或 findFragmentByTag()(對(duì)于提供或不提供 UI 的片段)獲取 Activity 中存在的 fragment 。
  • 通過 popBackStack()(模擬用戶發(fā)出的返回命令)將片段從返回棧中彈出甸赃。
  • 通過 addOnBackStackChangedListener() 注冊(cè)一個(gè)偵聽返回棧變化的偵聽器。

如需了解有關(guān)這些方法以及其他方法的詳細(xì)信息冗酿,請(qǐng)參閱 FragmentManager 類文檔埠对。

執(zhí)行fragment事務(wù)


提交給 Activity 的每組更改都稱為事務(wù),您可以使用 FragmentTransaction 中的 API 來執(zhí)行一項(xiàng)事務(wù)裁替。您也可以將每個(gè)事務(wù)保存到由 Activity 管理的返回棧內(nèi)项玛,從而讓用戶能夠回退 fragment (類似于回退 Activity)。

從 FragmentManager 獲取一個(gè) FragmentTransaction 實(shí)例:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每個(gè)事務(wù)都是您想要同時(shí)執(zhí)行的一組更改弱判。您可以使用 add()襟沮、remove() 和 replace() 等方法為給定事務(wù)設(shè)置您想要執(zhí)行的所有更改。然后,要想將事務(wù)應(yīng)用到 Activity开伏,您必須調(diào)用 commit()膀跌。

不過,在您調(diào)用 commit() 之前固灵,您可能想調(diào)用 addToBackStack()捅伤,以將事務(wù)添加到片段事務(wù)返回棧。 該返回棧由 Activity 管理巫玻,允許用戶通過按返回按鈕返回上一片段狀態(tài)丛忆。

例如,以下示例說明了如何將一個(gè) fragment 替換成另一個(gè) fragment 仍秤,以及如何在返回棧中保留先前狀態(tài):

// 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 會(huì)替換目前在 R.id.fragment_container ID 所標(biāo)識(shí)的布局容器中的任何 fragment (如有)。通過調(diào)用 addToBackStack() 可將替換事務(wù)保存到返回棧诗力,以便用戶能夠通過按返回按鈕撤消事務(wù)并回退到上一 fragment 凰浮。

如果您向事務(wù)添加了多個(gè)更改(如又一個(gè) add() 或 remove()),并且調(diào)用了 addToBackStack()姜骡,則在調(diào)用 commit() 前應(yīng)用的所有更改都將作為單一事務(wù)添加到返回棧导坟,并且返回按鈕會(huì)將它們一并撤消。

向 FragmentTransaction 添加更改的順序無關(guān)緊要圈澈,不過:

  • 您必須最后調(diào)用 commit()
  • 如果您要向同一容器添加多個(gè) fragment 惫周,則您添加 fragment 的順序?qū)Q定它們?cè)谝晥D層次結(jié)構(gòu)中的出現(xiàn)順序

如果您沒有在執(zhí)行移除 fragment 的事務(wù)時(shí)調(diào)用 addToBackStack(),則事務(wù)提交時(shí)該 fragment 會(huì)被銷毀康栈,用戶將無法回退到該 fragment 递递。 否則(即調(diào)用了addToBackStack()),則系統(tǒng)會(huì)停止該調(diào)用 addToBackStack()啥么,并在用戶回退時(shí)將其恢復(fù)登舞。

提示:對(duì)于每個(gè) fragment 事務(wù),您都可以通過在提交前調(diào)用 setTransition() 來應(yīng)用過渡動(dòng)畫悬荣。

調(diào)用 commit() 不會(huì)立即執(zhí)行事務(wù)菠秒,而是在 Activity 的 UI 線程(“主”線程)可以執(zhí)行該操作時(shí)再安排其在線程上運(yùn)行。不過氯迂,如有必要践叠,您也可以從 UI 線程調(diào)用 executePendingTransactions() 以立即執(zhí)行 commit() 提交的事務(wù)。通常不必這樣做嚼蚀,除非其他線程中的作業(yè)依賴該事務(wù)禁灼。

注意:您只能在 Activity 保存其狀態(tài)(用戶離開 Activity)之前使用 commit() 提交事務(wù)。如果您試圖在該時(shí)間點(diǎn)后提交轿曙,則會(huì)引發(fā)異常弄捕。 這是因?yàn)槿缧杌謴?fù) Activity僻孝,則提交后的狀態(tài)可能會(huì)丟失。 對(duì)于丟失提交無關(guān)緊要的情況守谓,請(qǐng)使用 commitAllowingStateLoss()穿铆。

FragmentTransaction中的常用方法:

  • add():往Activity中添加一個(gè)Fragment。
  • remove():從Activity中移除一個(gè)Fragment分飞,如果被移除的Fragment沒有添加到回退棧悴务,這個(gè)Fragment實(shí)例將會(huì)被銷毀。
  • replace():使用另一個(gè)Fragment替換當(dāng)前的譬猫,實(shí)際上就是remove()然后add()的合體讯檐。
  • hide():隱藏當(dāng)前的Fragment,僅僅是設(shè)為不可見染服,并不會(huì)銷毀别洪。
  • show():顯示之前隱藏的Fragment
  • detach():將此Fragment從Activity中分離,會(huì)銷毀其布局柳刮,但不會(huì)銷毀該實(shí)例挖垛。
  • attach():將從Activity中分離的Fragment,重新關(guān)聯(lián)到該Activity秉颗,重新創(chuàng)建其視圖層次痢毒。
  • commit():提交一個(gè)事務(wù)。

Fragment的靜態(tài)加載

以上使用是動(dòng)態(tài)加載fragment蚕甥,還可以如下靜態(tài)加載fragment哪替。
靜態(tài)加載Fragment分三步:

  1. 創(chuàng)建一個(gè)Layout,作為要靜態(tài)加載的Fragment的布局菇怀。
  2. 創(chuàng)建一個(gè)類繼承Fragment凭舶,然后重寫里面的onCreateView方法,View對(duì)象為剛剛創(chuàng)建的Layout爱沟。
  3. 在Layout布局文件中聲明fragment帅霜,android:name屬性里是我們上面創(chuàng)建的類,另外呼伸,fragment必須用id或tag作為唯一標(biāo)識(shí)身冀。

與 Activity 通信


盡管 Fragment 是作為獨(dú)立于 Activity 的對(duì)象實(shí)現(xiàn),并且可在多個(gè) Activity 內(nèi)使用括享,但fragment的給定實(shí)例會(huì)直接綁定到包含它的 Activity闽铐。

具體地說,fragment可以通過 getActivity() 訪問 Activity 實(shí)例奶浦,并輕松地執(zhí)行在 Activity 布局中查找視圖等任務(wù)。

View listView = getActivity().findViewById(R.id.list);

同樣地踢星,您的 Activity 也可以使用 findFragmentById() 或 findFragmentByTag()澳叉,通過從 FragmentManager 獲取對(duì) Fragment 的引用來調(diào)用片段中的方法。例如:

ExampleFragment fragment = (ExampleFragment) 
                 getFragmentManager().findFragmentById(R.id.example_fragment);

創(chuàng)建對(duì)Activity的事件回調(diào)

在某些情況下,您可能需要通過 fragment 與 Activity 共享事件成洗。執(zhí)行此操作的一個(gè)好方法是五督,在 fragment 內(nèi)定義一個(gè)回調(diào)接口,并要求宿主 Activity 實(shí)現(xiàn)它瓶殃。 當(dāng) Activity 通過該接口收到回調(diào)時(shí)充包,可以根據(jù)需要與布局中的其他 fragment 共享這些信息。

例如遥椿,如果一個(gè)新聞應(yīng)用的 Activity 有兩個(gè) fragment —— 一個(gè)用于顯示文章列表( fragment A)基矮,另一個(gè)用于顯示文章( fragment B),那么 fragment A 必須在列表項(xiàng)被選定后告知 Activity冠场,以便它告知 fragment B 顯示該文章家浇。 在本例中,OnArticleSelectedListener 接口在 fragment A 內(nèi)聲明:

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

如果 Activity 未實(shí)現(xiàn)接口碴裙,則 fragment 會(huì)引發(fā) ClassCastException钢悲。實(shí)現(xiàn)時(shí),mListener 成員會(huì)保留對(duì) Activity 的 OnArticleSelectedListener 實(shí)現(xiàn)的引用舔株,以便 fragment A 可以通過調(diào)用 OnArticleSelectedListener 接口定義的方法與 Activity 共享事件莺琳。例如,如果 fragment A 是 ListFragment 的一個(gè)擴(kuò)展载慈,則用戶每次點(diǎn)擊列表項(xiàng)時(shí)惭等,系統(tǒng)都會(huì)調(diào)用 fragment 中的 onListItemClick(),然后該方法會(huì)調(diào)用 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);
    }
    ...
}

傳遞到 onListItemClick() 的 id 參數(shù)是被點(diǎn)擊項(xiàng)的行 ID娃肿,即 Activity(或其他fragment)用來從應(yīng)用的 ContentProvider 獲取文章的 ID咕缎。

Fragment之間使用Bundle傳遞數(shù)據(jù)

FragmentOne:在用戶點(diǎn)擊按鈕時(shí),將fragment2添加到當(dāng)前頁面顯示出來料扰,并傳遞一個(gè)Bundle對(duì)象凭豪。

@Override
public void onClick(View v)
{
    //FragmentTwo是從FragmentOne即將跳轉(zhuǎn)到的第二個(gè)Fragment
    FragmentTwo fTwo = new FragmentTwo();
    //利用Bundle傳遞“key”中保存的參數(shù)Path中數(shù)據(jù)
    Bundle bundle = new Bundle();
    bundle.putString("key", Path);
    fTwo.setArguments(bundle);

    getFragmentManager().beginTransaction()
    .replace(R.id.id_content, fTwo, "TWO");
    .addToBackStack(null);
    .commit();
}

FragmentTwo:在FragmentTwo中獲取傳遞的參數(shù):

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.right, null);
    TextView textView = (TextView) view.findViewById(R.id.textView1);
    Bundle bundle = getArguments();
    if (bundle != null) {
        String item = bundle.getString("key");
        textView.setText(item);
    }
    return view;
}

向App Bar添加菜單

您的fragment可以通過實(shí)現(xiàn) onCreateOptionsMenu() 向 Activity 的選項(xiàng)菜單(并因此向應(yīng)用欄)貢獻(xiàn)菜單項(xiàng)。不過晒杈,為了使此方法能夠收到調(diào)用嫂伞,您必須在 onCreate() 期間調(diào)用 setHasOptionsMenu(),以指示片段想要向選項(xiàng)菜單添加菜單項(xiàng)(否則拯钻,fragment將不會(huì)收到對(duì) onCreateOptionsMenu() 的調(diào)用)帖努。

您之后從fragment添加到選項(xiàng)菜單的任何菜單項(xiàng)都將追加到現(xiàn)有菜單項(xiàng)之后。 選定菜單項(xiàng)時(shí)粪般,fragment還會(huì)收到對(duì) onOptionsItemSelected() 的回調(diào)拼余。

您還可以通過調(diào)用 registerForContextMenu(),在fragment布局中注冊(cè)一個(gè)視圖來提供浮動(dòng)上下文菜單亩歹。用戶打開上下文菜單時(shí)匙监,fragment會(huì)收到對(duì) onCreateContextMenu()}的調(diào)用凡橱。當(dāng)用戶選擇某個(gè)菜單項(xiàng)時(shí),fragment會(huì)收到對(duì) onContextItemSelected() 的調(diào)用亭姥。

處理fragment生命周期


管理fragment生命周期與管理 Activity 生命周期很相似稼钩。和 Activity 一樣,fragment也以三種狀態(tài)存在:

  • Resume
    fragment在運(yùn)行中的 Activity 中可見达罗。
  • Pause
    另一個(gè) Activity 位于前臺(tái)并具有焦點(diǎn)坝撑,但此fragment所在的 Activity 仍然可見(前臺(tái) Activity 部分透明,或未覆蓋整個(gè)屏幕)粮揉。
  • Stop
    fragment不可見巡李。宿主 Activity 已停止,或fragment已從 Activity 中移除滔蝉,但已添加到返回棧击儡。 停止fragment仍然處于存活狀態(tài)(系統(tǒng)會(huì)保留所有狀態(tài)和成員信息)。 不過蝠引,它對(duì)用戶不再可見阳谍,如果 Activity 被終止,它也會(huì)被終止螃概。
    同樣與 Activity 一樣矫夯,假使 Activity 的進(jìn)程被終止,而您需要在重建 Activity 時(shí)恢復(fù)片段狀態(tài)吊洼,您也可以使用 Bundle 保留片段的狀態(tài)训貌。您可以在fragment的 onSaveInstanceState() 回調(diào)期間保存狀態(tài),并可在 onCreate()冒窍、onCreateView() 或 onActivityCreated() 期間恢復(fù)狀態(tài)递沪。

Activity 生命周期與fragment生命周期之間的最顯著差異在于它們?cè)谄涓髯苑祷貤V械拇鎯?chǔ)方式。 默認(rèn)情況下综液,Activity 停止時(shí)會(huì)被放入由系統(tǒng)管理的 Activity 返回棧(以便用戶通過返回按鈕回退到 Activity款慨,任務(wù)和返回棧對(duì)此做了闡述)。然而谬莹,僅當(dāng)您在移除fragment的事務(wù)執(zhí)行期間通過調(diào)用 addToBackStack() 顯式請(qǐng)求保存實(shí)例時(shí)檩奠,系統(tǒng)才會(huì)將fragment放入由宿主 Activity 管理的返回棧。

在其他方面附帽,管理fragment生命周期與管理 Activity 生命周期非常相似埠戳。 因此,管理 Activity 生命周期的做法同樣適用于fragment蕉扮。 但您還需要了解 Activity 的生命周期對(duì)fragment生命周期的影響整胃。

注意:如需 Fragment 內(nèi)的某個(gè) Context 對(duì)象,可以調(diào)用 getActivity()喳钟。但要注意爪模,請(qǐng)僅在fragment附加到 Activity 時(shí)調(diào)用 getActivity()欠啤。如果fragment尚未附加,或在其生命周期結(jié)束期間分離屋灌,則 getActivity() 將返回 null。

與 Activity 生命周期協(xié)調(diào)一致

圖 3

fragment所在的 Activity 的生命周期會(huì)直接影響fragment的生命周期应狱,其表現(xiàn)為共郭,Activity 的每次生命周期回調(diào)都會(huì)引發(fā)每個(gè)fragment的類似回調(diào)。 例如疾呻,當(dāng) Activity 收到 onPause() 時(shí)除嘹,Activity 中的每個(gè)fragment也會(huì)收到 onPause()。

不過岸蜗,fragment還有幾個(gè)額外的生命周期回調(diào)尉咕,用于處理與 Activity 的唯一交互,以執(zhí)行構(gòu)建和銷毀片段 UI 等操作璃岳。 這些額外的回調(diào)方法是:

  • onAttach()
    在fragment已與 Activity 關(guān)聯(lián)時(shí)調(diào)用(Activity 傳遞到此方法內(nèi))年缎。
  • onCreateView()
    調(diào)用它可創(chuàng)建與fragment關(guān)聯(lián)的視圖層次結(jié)構(gòu)。
  • onActivityCreated()
    在 Activity 的 onCreate() 方法已返回時(shí)調(diào)用铃慷。
  • onDestroyView()
    在移除與fragment關(guān)聯(lián)的視圖層次結(jié)構(gòu)時(shí)調(diào)用单芜。
  • onDetach()
    在取消fragment與 Activity 的關(guān)聯(lián)時(shí)調(diào)用。

圖 3 圖示說明了受其宿主 Activity 影響的fragment生命周期犁柜。在該圖中洲鸠,您可以看到 Activity 的每個(gè)連續(xù)狀態(tài)如何決定fragment可以收到的回調(diào)方法。 例如馋缅,當(dāng) Activity 收到其 onCreate() 回調(diào)時(shí)扒腕,Activity 中的fragment只會(huì)收到 onActivityCreated() 回調(diào)。

一旦 Activity 達(dá)到resumed狀態(tài)萤悴,您就可以隨意向 Activity 添加fragment和移除其中的fragment瘾腰。 因此,只有當(dāng) Activity 處于resumed狀態(tài)時(shí)稚疹,fragment的生命周期才能獨(dú)立變化居灯。

不過,當(dāng) Activity 離開resumed狀態(tài)時(shí)内狗,fragment會(huì)在 Activity 的推動(dòng)下再次經(jīng)歷其生命周期怪嫌。

Fragment的向下兼容

  1. 當(dāng)對(duì)Fragment引用包的時(shí)候,會(huì)有兩個(gè)選項(xiàng)柳沙,android.app.Fragment和android.support.v4.app.Fragment岩灭,其中android.support.v4.app.Fragment就是為了兼容低版本(當(dāng)API Version 低于11時(shí))而考慮的。
  2. 使用android.support.v4.app.Fragment時(shí)赂鲤,Activity需要繼承FragmentActivity噪径,如果Activity繼承了AppCompatActivity就不用了柱恤,AppCompatActivity已經(jīng)繼承了FragmentActivity。

順便推薦簡(jiǎn)書上關(guān)于Fragment的一個(gè)系列:
Fragment完全解析系列一
Fragment完全解析系列二
Fragment之我的解決方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末找爱,一起剝皮案震驚了整個(gè)濱河市梗顺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌车摄,老刑警劉巖寺谤,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吮播,居然都是意外死亡变屁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門意狠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粟关,“玉大人,你說我怎么就攤上這事环戈∶瓢澹” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵谷市,是天一觀的道長(zhǎng)蛔垢。 經(jīng)常有香客問我,道長(zhǎng)迫悠,這世上最難降的妖魔是什么鹏漆? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮创泄,結(jié)果婚禮上艺玲,老公的妹妹穿的比我還像新娘。我一直安慰自己鞠抑,他們只是感情好饭聚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搁拙,像睡著了一般秒梳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箕速,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天酪碘,我揣著相機(jī)與錄音,去河邊找鬼盐茎。 笑死兴垦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播探越,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼狡赐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了钦幔?” 一聲冷哼從身側(cè)響起枕屉,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲤氢,沒想到半個(gè)月后搀庶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铜异,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秸架。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揍庄。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖东抹,靈堂內(nèi)的尸體忽然破棺而出蚂子,到底是詐尸還是另有隱情,我是刑警寧澤缭黔,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布食茎,位于F島的核電站,受9級(jí)特大地震影響馏谨,放射性物質(zhì)發(fā)生泄漏别渔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一惧互、第九天 我趴在偏房一處隱蔽的房頂上張望哎媚。 院中可真熱鬧,春花似錦喊儡、人聲如沸拨与。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽买喧。三九已至,卻和暖如春匆赃,著一層夾襖步出監(jiān)牢的瞬間淤毛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工炸庞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钱床,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓埠居,卻偏偏與公主長(zhǎng)得像查牌,于是被迫代替她去往敵國(guó)和親事期。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344