Android從3.0開始引入fragment,主要是爲了支持更動態更靈活的界面設計,好比在平板上的應用。平板機上擁有比手機更大的屏幕空間來組合和交互界面組件們。Fragment使你在作那樣的設計時,不需應付view樹中複雜的變化。經過把activity的layout分紅fragment,你能夠在activity運行時改變它的樣子,而且能夠在activity的後退棧中保存這些改變。html
例如:寫一個讀新聞的程序,能夠用一個fragment顯示標題列表,另外一個fragment顯示選中標題的內容,這兩個fragment都在一個activity上,並排顯示。那麼這兩個fragment都有本身的生命週期並響應本身的事件。因而,不需再像手機上那樣用一個activity顯示標題列表,用另外一個activity顯示新聞內容;如今能夠把二者放在一個activity上同時顯示出來。以下圖:java
Fragment必須被寫成可重用的模塊。由於fragment有本身的layout,本身進行事件響應,擁有本身的生命週期和行爲,因此你能夠在多個activity中包含同一個Fragment的不一樣實例。這對於讓你的界面在不一樣的屏幕尺寸下都能給用戶完美的體驗尤爲重要。好比你能夠在程序運行於大屏幕中時啓動包含不少fragment的activity,而在運行於小屏幕時啓動一個包含少許fragment的activity。
android
舉個例子--仍是剛纔那個讀新聞的程序-當你檢測到程序運行於大屏幕時,啓動activityA,你將標題列表和新聞內容這兩個fragment都放在activityA中;當檢測到程序運行於小屏幕時,仍是啓動activityA,但此時A中只有標題列表fragment,當選中一個標題時,activityA啓動activityB,B中含有新聞內容fragment。數據庫
Fragment是activity的界面中的一部分或一種行爲。你能夠把多個Fragment組合到一個activity中來建立一個多面界面而且你能夠在多個activity中重用一個Fragment。你能夠把Fragment認爲模塊化的一段activity,它具備本身的生命週期,接收它本身的事件,並能夠在activity運行時被添加或刪除。ide
Fragment不能獨立存在,它必須嵌入到activity中,並且Fragment的生命週期直接受所在的activity的影響。例如:當activity暫停時,它擁有的全部的Fragment都暫停了,當activity銷燬時,它擁有的全部Fragment都被銷燬。然而,當activity運行時(在onResume()以後,onPause()以前),你能夠單獨地操做每一個Fragment,好比添加或刪除它們。當你在執行上述針對Fragment的事務時,你能夠將事務添加到一個棧中,這個棧被activity管理,棧中的每一條都是一個Fragment的一次事務。有了這個棧,就能夠反向執行Fragment的事務,這樣就能夠在Fragment級支持「返回」鍵(向後導航)。模塊化
當向activity中添加一個Fragment時,它須置於ViewGroup控件中,而且需定義Fragment本身的界面。你能夠在layoutxml文件中聲明Fragment,元素爲:<fragment>;也能夠在代碼中建立Fragment,而後把它加入到ViewGroup控件中。然而,Fragment不必定非要放在activity的界面中,它能夠隱藏在後臺爲actvitiy工做。函數
要建立fragment,必須從Fragment或Fragment的派生類派生出一個類。Fragment的代碼寫起來有些像activity。它具備跟activity同樣的回調方法,好比 onCreate(),onStart(),onPause()和onStop()。實際上,若是你想把老的程序改成使用fragment,基本上只須要把activity的回調方法的代碼移到fragment中對應的方法便可。動畫
一般須要實現如下生命週期函數:this
onCreate():當建立fragment時系統調用此方法。在其中你必須初始化fragment的基礎組件。spa
onCreateView():系統在fragment要畫本身的界面時調用(在真正顯示以前)此方法。這個方法必須返回frament的layout的根控件。若是這個fragment不提供界面,那它應返回null。
onPause():不廢話了,跟activity同樣。
大多數程序應最少對fragment實現這三個方法。固然還有其它幾個回調方法可應該按狀況實現。
下圖爲fragment的生命週期
還有幾個現成的fragment的派生類,你可能須要從它們派生,以下所列:
DialogFragment 顯示一個浮動的對話框。使用這個類建立對話框是替代activity建立對話框的最佳選擇.由於你能夠把fragmentdialog放入到activity的返回棧中,使用戶能再返回到這個對話框。
ListFragment 顯示一個列表控件,就像ListActivity類,它提供了不少管理列表的方法,好比onListItemClick()方法響應click事件。
PreferenceFragment 顯示一個由Preference對象組成的列表,與PreferenceActivity相同。它用於爲程序建立「設置」activity。
fragment通常做爲activity的用戶界面的一部分,把它本身的layout嵌入到activity的layout中。 要爲fragment提供layout,你必須實現onCreateView()回調方法,而後在這個方法中返回一個View對象,這個對象是fragment的layout的根。
注:若是你的fragment是從ListFragment中派生的,就不須要實現onCreateView()方法了,由於默認的實現已經爲你返回了ListView控件對象。
要從onCreateView()方法中返回layout對象,你能夠從layoutxml中生成layout對象。爲了幫助你這樣作,onCreateView()提供了一個LayoutInflater對象。
舉例:如下代碼展現了一個Fragment的子類如何從layoutxml文件example_fragment.xml中生成對象。
Public static class ExampleFragment extends Fragment{ @Override public View onCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState){ //Inflate the layout for this fragment returninflater.inflate(R.layout.example_fragment,container,false); } }
onCreateView()參數中的container是存放fragment的layout的ViewGroup對象。savedInstanceState參數是一個Bundle,跟activity的onCreate()中Bundle差很少,用於狀態恢復。可是fragment的onCreate()中也有Bundle參數,因此此處的Bundle中存放的數據與onCreate()中存放的數據仍是不一樣的(我猜的)。
Inflate()方法有三個參數:
1 layout的資源ID。
2 存放fragment的layout的ViewGroup。
3 布爾型數據表示是否在建立fragment的layout期間,把layout附加到container上(在這個例子中,由於系統已經把layout插入到container中了,因此值爲false,若是爲true會導至在最終的layout中建立多餘的ViewGroup(這句我看不明白,但我翻譯的應該沒錯))。
通常狀況下,fragment把它的layout做爲activitiy的layout的一部分合併到activity中,有兩種方法將一個fragment添加到activity中:
以下代碼,一個activity中包含兩個fragment:
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragmentandroid:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent"/> <fragmentandroid:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent"/> </LinearLayout>
當系統建立上例中的layout時,它實例化每個fragment,而後調用它們的onCreateView()方法,以獲取每一個fragment的layout。系統把fragment返回的view對象插入到<fragment>元素的位置,直接代替<fragment>元素。
注:每一個fragment都須要提供一個ID,系統在activity從新建立時用它來恢復fragment們,你也能夠用它來操做fragment進行其它的事物,好比刪除它。有三種方法給fragment提供ID:
1 爲android:id屬性賦一個數字。
2 爲android:tag屬性賦一個字符串。
3 若是你沒有使用上述任何一種方法,系統將使用fragment的容器的ID。
這種方法能夠在運行時,把fragment添加到activity的layout中。你只需指定一個要包含fragment的ViewGroup。爲了完成fragment的事務(好比添加,刪除,替換等),你必須使用FragmentTransaction的方法。你能夠從activity獲取到FragmentTransaction,以下:
FragmentManager fragmentManager =getFragmentManager(); FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
而後你能夠用add()方法添加一個fragment,它有參數用於指定容納fragment的ViewGroup。以下:
ExampleFragment fragment =new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container,fragment); fragmentTransaction.commit();
Add()的第一個參數是容器ViewGroup,第二個是要添加的fragment。一旦你經過FragmentTransaction對fragment作出了改變,你必須調用方法commit()提交這些改變。
不只在無界面的fragment中,在有界面的fragment中也可使用tag來做爲爲一標誌,這樣在須要獲取fragment對象時,要調用findFragmentTag()。
上面演示瞭如何添加fragment來提供界面,然而,你也可使用fragment爲activity提供後臺的行爲而不用顯示fragment的界面。
要添加一個沒有界面的fragment,需在activity中調用方法add(Fragment,String)(它支持用一個惟一的字符串作爲fragment的」tag」,而不是viewID)。這樣添加的fragment因爲沒有界面,因此你在實現它時不需調用實現onCreateView()方法。 使用tag字符串來標識一個fragment並非只能用於沒有界面的fragment上,你也能夠把它用於有界面的fragment上,可是,若是一個fragment沒有界面,tag字符串將成爲它惟一的選擇。獲取以tag標識的fragment,需使用方法findFragmentByTab()。
要管理fragment,需使用FragmentManager,要獲取它,需在activity中調用方法getFragmentManager()。
你能夠用FragmentManager來作如下事情:
1 使用方法findFragmentById()或findFragmentByTag(),獲取activity中已存在的fragment。
2 使用方法popBackStack()從activity的後退棧中彈出fragment(這能夠模擬後退鍵引起的動做)。
3 用方法addOnBackStackChangedListerner()註冊一個偵聽器以監視後退棧的變化。
在activity中使用fragment的一個偉大的好處是能跟據用戶的輸入對fragment進行添加、刪除、替換以及執行其它動做的能力。你提交的一組fragment的變化叫作一個事務。事務經過FragmentTransaction來執行。你還能夠把每一個事務保存在activity的後退棧中,這樣就可讓用戶在fragment變化之間導航(跟在activity之間導航同樣)。
你能夠經過FragmentManager來取得FragmentTransaction的實例,以下:
FragmentManager fragmentManager=getFragmentManager(); FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
一個事務是在同一時刻執行的一組動做(很像數據庫中的事務)。你能夠用add(),remove(),replace()等方法構成事務,最後使用commit()方法提交事務。
在調用commint()以前,你能夠用addToBackStack()把事務添加到一個後退棧中,這個後退棧屬於所在的activity。有了它,就能夠在用戶按下返回鍵時,返回到fragment們執行事務以前的狀態。
以下例:演示瞭如何用一個fragment代替另外一個fragment,同時在後退棧中保存被代替的fragment的狀態。
//Create new fragment and transaction Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); //Replace whatever is in the fragment_container view with thisfragment, //and add the transaction to the backstack transaction.replace(R.id.fragment_container,newFragment); transaction.addToBackStack(null); //Commit the transaction transaction.commit();
解釋:newFragment代替了控件IDR.id.fragment_container所指向的ViewGroup中所含的任何fragment。而後調用addToBackStack(),此時被代替的fragment就被放入後退棧中,因而當用戶按下返回鍵時,事務發生回溯,原先的fragment又回來了。
若是你向事務添加了多個動做,好比屢次調用了add(),remove()等以後又調用了addToBackStack()方法,那麼全部的在commit()以前調用的方法都被做爲一個事務。當用戶按返回鍵時,全部的動做都被反向執行(事務回溯)。
事務中動做的執行順序可隨意,但要注意如下兩點:
1 你必須最後調用commit()。
2 若是你添加了多個fragment,那麼它們的顯示順序跟添加順序一至(後顯示的覆蓋前面的)。
若是你在執行的事務中有刪除fragment的動做,並且沒有調用addToBackStack(),那麼當事務提交時,那些被刪除的fragment就被銷燬了。反之,那些fragment就不會被銷燬,而是處於中止狀態。當用戶返回時,它們會被恢復。
密技:對於fragment事務,你能夠應用動畫。在commit()以前調用setTransition()就行。――通常銀我不告訴他哦。
可是,調用commit()後,事務並不會立刻執行。它會在activity的UI線程(其實就是主線程)中等待直到線程能執行的時候才執行(廢話)。若是必要,你能夠在UI線程中調用executePendingTransactions()方法來當即執行事務。但通常不需這樣作,除非有其它線程在等待事務的執行。
警告:你只能在activity處於可保存狀態的狀態時,好比running中,onPause()方法和onStop()方法中提交事務,不然會引起異常。這是由於fragment的狀態會丟失。若是要在可能丟失狀態的狀況下提交事務,請使用commitAllowingStateLoss()。