Fragment 全解析

1.概述

Fragment是Activity中用戶(hù)界面的一個(gè)行為或者是一部分。主要是支持在大屏幕上動(dòng)態(tài)和更為靈活的去組合或是交換UI組件,通過(guò)將activity的布局分割成若干個(gè)fragment,可以在運(yùn)行時(shí)編輯activity的呈現(xiàn),并且那些變化會(huì)被保存在由activity管理的后臺(tái)棧里面。
  Fragment必須總是被嵌入到一個(gè)activity之中,并且fragment的生命周期直接受其宿主activity的生命周期的影響。你可以認(rèn)為fragment是activity的一個(gè)模塊零件,它有自己的生命周期,接收它自己的輸入事件,并且可以在activity運(yùn)行時(shí)添加或者刪除。
  應(yīng)該將每一個(gè)fragment設(shè)計(jì)為模塊化的和可復(fù)用化的activity組件。也就是說(shuō),你可以在多個(gè)activity中引用同一個(gè)fragment,因?yàn)閒ragment定義了它自己的布局,并且使用它本身生命周期回調(diào)的行為。

2.Fragment的生命周期

先看fragment生命周期圖:


  fragment所生存的activity生命周期直接影響著fragment的生命周期,由此針對(duì)activity的每一個(gè)生命周期回調(diào)都會(huì)引發(fā)一個(gè)fragment類(lèi)似的回調(diào)。例如,當(dāng)activity接收到onPause()時(shí),這個(gè)activity之中的每個(gè)fragment都會(huì)接收到onPause()。   這有Activity的詳細(xì)說(shuō)明
  Fragment有一些額外的生命周期回調(diào)方法(創(chuàng)建和銷(xiāo)毀fragment界面).
onAttach()

當(dāng)fragment被綁定到activity時(shí)調(diào)用(Activity會(huì)被傳入)。
onCreateView()

將本身的布局構(gòu)建到activity中去(fragment作為activity界面的一部分)   
onActivityCreated()

當(dāng)activity的onCreate()函數(shù)返回時(shí)被調(diào)用。
onDestroyView()

當(dāng)與fragment關(guān)聯(lián)的視圖體系正被移除時(shí)被調(diào)用。
onDetach()

當(dāng)fragment正與activity解除關(guān)聯(lián)時(shí)被調(diào)用。
當(dāng)activity接收到它的onCreate()回調(diào)時(shí),activity之中的fragment接收到onActivityCreated()回調(diào)。
  一旦activity處于resumed狀態(tài),則可以在activity中自由的添加或者移除fragment。因此,只有當(dāng)activity處于resumed狀態(tài)時(shí),fragment的生命周期才可以獨(dú)立變化。    fragment會(huì)在 activity離開(kāi)恢復(fù)狀態(tài)時(shí) 再一次被activity推入它的生命周期中。
管理fragment生命周期與管理activity生命周期很相像。像activity一樣,fragment也有三種狀態(tài):
Resumed

fragment在運(yùn)行中的activity可見(jiàn)。
Paused

另一個(gè)activity處于前臺(tái)且得到焦點(diǎn),但是這個(gè)fragment所在的activity仍然可見(jiàn)(前臺(tái)activity部分透明,或者沒(méi)有覆蓋全屏)。
Stopped

fragment不可見(jiàn)。要么宿主activity已經(jīng)停止,要么fragment已經(jīng)從activity上移除,但已被添加到后臺(tái)棧中。一個(gè)停止的fragment仍然活著(所有狀態(tài)和成員信息仍然由系統(tǒng)保留著)。但是,它對(duì)用戶(hù)來(lái)講已經(jīng)不再可見(jiàn),并且如果activity被殺掉,它也將被殺掉。
  如果activity的進(jìn)程被殺掉了,在activity被重新創(chuàng)建時(shí),你需要恢復(fù)fragment狀態(tài)。可以執(zhí)行fragment的onSaveInstanceState()來(lái)保存狀態(tài)(注意在fragment是在onCreate(),onCreateView(),或onActvityCreate()中進(jìn)行恢復(fù))。
  在生命周期方面,activity與fragment之間一個(gè)很重要的不同,就是在各自的后臺(tái)棧中是如何存儲(chǔ)的。   當(dāng)activity停止時(shí),默認(rèn)情況下activity被安置在由系統(tǒng)管理的activity后臺(tái)棧中;    fragment僅當(dāng)在一個(gè)事務(wù)被移除時(shí),通過(guò)顯式調(diào)用addToBackStack()請(qǐng)求保存的實(shí)例,該fragment才被置于由宿主activity管理的后臺(tái)棧。 
要?jiǎng)?chuàng)建一個(gè)fragment,必須創(chuàng)建一個(gè)fragment的子類(lèi)。一般情況下,我們至少需要實(shí)現(xiàn)以下幾個(gè)fragment生命周期方法:
onCreate()

在創(chuàng)建fragment時(shí)系統(tǒng)會(huì)調(diào)用此方法。在實(shí)現(xiàn)代碼中,你可以初始化想要在fragment中保持的那些必要組件,當(dāng)fragment處于暫停或者停止?fàn)顟B(tài)之后可重新啟用它們。
onCreateView()

在第一次為fragment繪制用戶(hù)界面時(shí)系統(tǒng)會(huì)調(diào)用此方法。為fragment繪制用戶(hù)界面,這個(gè)函數(shù)必須要返回所繪出的fragment的根View。如果fragment沒(méi)有用戶(hù)界面可以返回空。
  
@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); }
inflate()函數(shù)需要以下三個(gè)參數(shù):
①要inflate的布局的資源ID。 
②被inflate的布局的父ViewGroup。
③一個(gè)布爾值,表明在inflate期間被infalte的布局是否應(yīng)該附上ViewGroup(第二個(gè)參數(shù)container)。(在這個(gè)例子中傳入的是false,因?yàn)橄到y(tǒng)已經(jīng)將被inflate的布局插入到容器中(container)——傳入true會(huì)在最終的布局里創(chuàng)建一個(gè)多余的ViewGroup。) 
onPause()

系統(tǒng)回調(diào)用該函數(shù)作為用戶(hù)離開(kāi)fragment的第一個(gè)預(yù)兆(盡管這并不總意味著fragment被銷(xiāo)毀)。在當(dāng)前用戶(hù)會(huì)話(huà)結(jié)束之前,通常要在這里提交任何應(yīng)該持久化的變化(因?yàn)橛脩?hù)可能不再返回)。

3.將fragment添加到activity之中

可以通過(guò)在activity布局文件中聲明fragment,用fragment標(biāo)簽把fragment插入到activity的布局中,或者是用應(yīng)用程序源碼將它添加到一個(gè)存在的ViewGroup中。       但fragment并不是一個(gè)定要作為activity布局的一部分,fragment也可以為activity隱身工作。

3.1在activity的布局文件里聲明fragment

可以像為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.test.FragmentOne" android:id="@+id/fo" android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
``

fragment標(biāo)簽中的android:name 屬性指定了布局中實(shí)例化的Fragment類(lèi)。
  當(dāng)系統(tǒng)創(chuàng)建activity布局時(shí),它實(shí)例化了布局文件中指定的每一個(gè)fragment,并為它們調(diào)用onCreateView()函數(shù),以獲取每一個(gè)fragment的布局。系統(tǒng)直接在元素的位置插入fragment返回的View。
  注意:每個(gè)fragment都需要一個(gè)唯一的標(biāo)識(shí),如果重啟activity,系統(tǒng)可用來(lái)恢復(fù)fragment(并且可用來(lái)捕捉fragment的事務(wù)處理,例如移除)。為fragment提供ID有三種方法:

用android:id屬性提供一個(gè)唯一的標(biāo)識(shí)。 
用android:tag屬性提供一個(gè)唯一的字符串。

如果上述兩個(gè)屬性都沒(méi)有,系統(tǒng)會(huì)使用其容器視圖(view)的ID。

3.2通過(guò)編碼將fragment添加到已存在的ViewGroup中

在activity運(yùn)行的任何時(shí)候,你都可以將fragment添加到activity布局中。      要管理activity中的fragment,可以使用FragmentManager。可以通過(guò)在activity中調(diào)用getFragmentManager()獲得。使用FragmentManager 可以做如下事情,包括:
使用findFragmentById()(用于在activity布局中提供有界面的fragment)或者findFragmentByTag()獲取activity中存在的fragment(用于有界面或者沒(méi)有界面的fragment)。

使用popBackStack()(模仿用戶(hù)的BACK命令)從后臺(tái)棧彈出fragment。

使用addOnBackStackChangedListener()注冊(cè)一個(gè)監(jiān)聽(tīng)后臺(tái)棧變化的監(jiān)聽(tīng)器。

在Android中,對(duì)Fragment的事務(wù)操作都是通過(guò)FragmentTransaction來(lái)執(zhí)行。操作大致可以分為兩類(lèi):
顯示:add() replace() show() attach()

隱藏:remove() hide() detach()

說(shuō)明:   調(diào)用show() & hide()方法時(shí),F(xiàn)ragment的生命周期方法并不會(huì)被執(zhí)行,僅僅是Fragment的View被顯示或者?隱藏。

執(zhí)行replace()時(shí)(至少兩個(gè)Fragment),會(huì)執(zhí)行第二個(gè)Fragment的onAttach()方法、執(zhí)行第一個(gè)Fragment的onPause()-onDetach()方法,同時(shí)containerView會(huì)detach第一個(gè)Fragment的View。

add()方法執(zhí)行onAttach()-onResume()的生命周期,相對(duì)的remove()就是執(zhí)行完成剩下的onPause()-onDetach()周期。

可以像下面這樣從Activity中取得FragmentTransaction的實(shí)例:
FragmentManager fragmentManager = getFragmentManager() FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
可以用add()函數(shù)添加fragment,并指定要添加的fragment以及要將其插入到哪個(gè)視圖(view)之中(注意commit事務(wù)):
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();

3.3添加沒(méi)有界面的fragment

也可以使用fragment為activity提供后臺(tái)動(dòng)作,卻不呈現(xiàn)多余的用戶(hù)界面。
  想要添加沒(méi)有界面的fragment ,可以使用add(Fragment, String)(為fragment提供一個(gè)唯一的字符串“tag”,而不是視圖(view)ID)。這樣添加了fragment,但是,因?yàn)檫€沒(méi)有關(guān)聯(lián)到activity布局中的視圖(view) ,收不到onCreateView()的調(diào)用。所以不需要實(shí)現(xiàn)這個(gè)方法。       對(duì)于無(wú)界面fragment,字符串標(biāo)簽是唯一識(shí)別它的方法。如果之后想從activity中取到fragment,需要使用findFragmentByTag()。

4.fragment事務(wù)后臺(tái)棧

在調(diào)用commit()之前,可以將事務(wù)添加到fragment事務(wù)后臺(tái)棧中(通過(guò)調(diào)用addToBackStatck())。這個(gè)后臺(tái)棧由activity管理,并且允許用戶(hù)通過(guò)按BACK鍵回退到前一個(gè)fragment狀態(tài)。
  下面的代碼中一個(gè)fragment代替另一個(gè)fragment,并且將之前的fragment狀態(tài)保留在后臺(tái)棧中:
Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); transaction.commit();
注意:
   如果添加多個(gè)變更事務(wù)(例如另一個(gè)add()或者remove())并調(diào)用addToBackStack(),那么在調(diào)用commit()之前的所有應(yīng)用的變更被作為一個(gè)單獨(dú)的事務(wù)添加到后臺(tái)棧中,并且BACK鍵可以將它們一起回退。
  當(dāng)移除一個(gè)fragment時(shí),如果調(diào)用了addToBackStack(),那么之后fragment會(huì)被停止,如果用戶(hù)回退,它將被恢復(fù)過(guò)來(lái)。
  調(diào)用commit()并不立刻執(zhí)行事務(wù),相反,而是采取預(yù)約方式,一旦activity的界面線(xiàn)程(主線(xiàn)程)準(zhǔn)備好便可運(yùn)行起來(lái)。然而,如果有必要的話(huà),你可以從界面線(xiàn)程調(diào)用executePendingTransations()立即執(zhí)行由commit()提交的事務(wù)。
  只能在activity保存狀態(tài)(當(dāng)用戶(hù)離開(kāi)activity時(shí))之前用commit()提交事務(wù)。如果你嘗試在那時(shí)之后提交,會(huì)拋出一個(gè)異常。這是因?yàn)槿绻鸻ctivity需要被恢復(fù),提交后的狀態(tài)會(huì)被丟失。對(duì)于這類(lèi)丟失提交的情況,可使用commitAllowingStateLoss()

5.與Activity交互

Activity中已經(jīng)有了該Fragment的引用,直接通過(guò)該引用進(jìn)行交互。

-如果沒(méi)引用可以通過(guò)調(diào)用fragment的函數(shù)findFragmentById()或者findFragmentByTag(),從FragmentManager中獲取Fragment的索引,例如:
 
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

在Fragment中可以通過(guò)getActivity得到當(dāng)前綁定的Activity的實(shí)例。

創(chuàng)建activity事件回調(diào)函數(shù),在fragment內(nèi)部定義一個(gè)回調(diào)接口,宿主activity來(lái)實(shí)現(xiàn)它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容