Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 最詳細的Fragment基礎講解

最詳細的Fragment基礎講解

編輯:關於Android編程

Fragment表現Activity中用UI的一個行為或者一部分.可以組合多個fragment放在一個單獨的activity中來創建一個多界面區域的UI,並可以在多個activity裡重用某一個fragment.把fragment想象成一個activity的模塊化區域,有它自己的生命周期,接收屬於它的輸入事件,並且可以在activity運行期間添加和刪除.


Fragment必須總是被嵌入到一個activity中,它們的生命周期直接被其所屬的宿主activity的生命周期影響.例如,當activity被暫停,那麼在其中的所有fragment也被暫停;當activity被銷毀,所有隸屬於它的fragment也被銷毀.然而,當一個activity正在運行時(處於resumed狀態),我們可以獨立地操作每一個fragment,比如添加或刪除它們.當處理這樣一個fragment事務時,也可以將它添加到activity所管理的back stack --每一個activity中的backstack實體都是一個發生過的fragment事務的記錄. back stack允許用戶通過按下BACK按鍵從一個fragment事務後退(往後導航).

將一個fragment作為activity布局的一部分添加進來時,它處在activity的viewhierarchy中的ViewGroup中,並且定義有它自己的view布局.通過在activity的布局文件中聲明fragment來插入一個fragment到你的activity布局中,或者可以寫代碼將它添加到一個已存在的ViewGroup.然而, fragment並不一定必須是activity布局的一部分;也可以將一個fragment作為activity的隱藏的後台工作者.

本文檔描述了如何使用fragment創建你的應用程序,包括:當被添加到activity的back stack後,fragment如何維護他們的狀態.在activity中,與activity和其他fragment共享事件.構建到activity的actionbar.以及更多內容.

 

設計哲學

Android在3.0中引入了fragments的概念,主要目的是用在大屏幕設備上--例如平板電腦上,支持更加動態和靈活的UI設計.平板電腦的屏幕要比手機的大得多,有更多的空間來放更多的UI組件,並且這些組件之間會產生更多的交互.Fragment允許這樣的一種設計,而不需要你親自來管理viewhierarchy的復雜變化.通過將activity的布局分散到fragment中,你可以在運行時修改activity的外觀,並在由activity管理的back stack中保存那些變化.

例如,一個新聞應用可以在屏幕左側使用一個fragment來展示一個文章的列表,然後在屏幕右側使用另一個fragment來展示一篇文章– 2個fragment並排顯示在相同的一個activity中,並且每一個fragment擁有它自己的一套生命周期回調方法,並且處理它們自己的用戶輸入事件.因此,取代使用一個activity來選擇一篇文章,而另一個activity來閱讀文章的方式,用戶可以在相同的activity中選擇一篇文章並且閱讀,如圖所示:

fragment在你的應用中應當是一個模塊化和可重用的組件.即,因為fragment定義了它自己的布局,以及通過使用它自己的生命周期回調方法定義了它自己的行為,你可以將fragment包含到多個activity中.這點特別重要,因為這允許你將你的用戶體驗適配到不同的屏幕尺寸.舉個例子,你可能會僅當在屏幕尺寸足夠大時,在一個activity中包含多個fragment,並且,當不屬於這種情況時,會啟動另一個單獨的,使用不同fragment的activity.

繼續之前那個新聞的例子--當運行在一個特別大的屏幕時(例如平板電腦),app可以在Activity A中嵌入2個fragment.然而,在一個正常尺寸的屏幕(例如手機)上,沒有足夠的空間同時供2個fragment用,因此, Activity A會僅包含文章列表的fragment,而當用戶選擇一篇文章時,它會啟動Activity B,它包含閱讀文章的fragment.因此,應用可以同時支持圖1中的2種設計模式

 

創建Fragment

要創建一個fragment,必須創建一個Fragment的子類(或者繼承自一個已存在的它的子類). Fragment類的代碼看起來很像Activity .它包含了和activity類似的回調方法,例如onCreate(), onStart(),onPause,以及onStop().事實上,如果你准備將一個現成的Android應用轉換到使用fragment,你可能只需簡單的將代碼從你的activity的回調函數分別移動到你的fragment的回調方法.

通常,應當至少實現如下的生命周期方法:

onCreate()
當創建fragment時,系統調用此方法.
在實現代碼中,應當初始化想要在fragment中保持的必要組件,當fragment被暫停或者停止後可以恢復.

onCreateView()
fragment第一次繪制它的用戶界面的時候,系統會調用此方法.為了繪制fragment的UI,此方法必須返回一個View,這個view是你的fragment布局的根view.如果fragment不提供UI,可以返回null.

onPause()
用戶將要離開fragment時,系統調用這個方法作為第一個指示(然而它不總是意味著fragment將被銷毀.)在當前用戶會話結束之前,通常應當在這裡提交任何應該持久化的變化(因為用戶有可能不會返回).


大多數應用應當為每一個fragment實現至少這3個方法,但是還有一些其他回調方法你也應當用來去處理fragment生命周期的各種階段.全部的生命周期回調方法將會在後面章節Handlingthe Fragment Lifecycle中討論.

除了繼承基類Fragment ,還有一些子類你可能會繼承:

DialogFragment
顯示一個浮動的對話框.
用這個類來創建一個對話框,是使用在Activity類的對話框工具方法之外的一個好的選擇,
因為你可以將一個fragment對話框合並到activity管理的fragment back stack中,允許用戶返回到一個之前曾被摒棄的fragment.

ListFragment
顯示一個由一個adapter(例如SimpleCursorAdapter)管理的項目的列表,類似於ListActivity.
它提供一些方法來管理一個list view,例如onListItemClick()回調來處理點擊事件.

PreferenceFragment
顯示一個Preference對象的層次結構的列表,類似於PreferenceActivity.
這在為你的應用創建一個"設置"activity時有用處.

 

添加一個用戶界面

fragment通常用來作為一個activity的用戶界面的一部分,並將它的layout提供給activity.為了給一個fragment提供一個layout,你必須實現onCreateView()回調方法,當到了fragment繪制它自己的layout的時候,Android系統調用它.你的此方法的實現代碼必須返回一個你的fragment的layout的根view.

注意:如果你的fragment是ListFragment的子類,它的默認實現是返回從onCreateView()返回一個ListView,所以一般情況下不必實現它.

從onCreateView()返回的View,也可以從一個xmllayout資源文件中讀取並生成.為了幫助你這麼做, onCreateView()提供了一個LayoutInflater對象.

舉個例子,這裡有一個Fragment的子類,從文件example_fragment.xml加載了一個layout:

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參數是你的fragmentlayout將被插入的父ViewGroup(來自activity的layout).savedInstanceState參數是一個Bundle,如果fragment是被恢復的,它提供關於fragment的之前的實例的數據,

inflate()方法有3個參數:

想要加載的layout的resource ID.

加載的layout的父ViewGroup.
傳入container是很重要的,目的是為了讓系統接受所要加載的layout的根view的layout參數,
由它將掛靠的父view指定.

布爾值指示在加載期間,展開的layout是否應當附著到ViewGroup (第二個參數).
(在這個例子中,指定了false,因為系統已經把展開的layout插入到container –傳入true會在最後的layout中創建一個多余的view group.)

將fragment添加到activity
通常地, fragment為宿主activity提供UI的一部分,被作為activity的整個viewhierarchy的一部分被嵌入.有2種方法你可以添加一個fragment到activitylayout:

在activity的layout文件中聲明fragment
你可以像為View一樣,為fragment指定layout屬性.
例子是一個有2個fragment的activity:

android:orientation="horizontal"

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/list"

android:layout_weight="1"

android:layout_width="0dp"

android:layout_height="match_parent" />

android:id="@+id/viewer"

android:layout_weight="2"

android:layout_width="0dp"

android:layout_height="match_parent" />

 

中的android:name屬性指定了在layout中實例化的Fragment類.

當系統創建這個activity layout時,它實例化每一個在layout中指定的fragment,並調用每一個上的onCreateView()方法,來獲取每一個fragment的layout.系統將從fragment返回的View直接插入到元素所在的地方.

注意:每一個fragment都需要一個唯一的標識,如果activity重啟,系統可以用來恢復fragment(並且你也可以用來捕獲fragment來處理事務,例如移除它.)

有3種方法來為一個fragment提供一個標識:

 

為android:id屬性提供一個唯一ID.

為android:tag屬性提供一個唯一字符串.

如果以上2個你都沒有提供,系統使用容器view的ID.


撰寫代碼將fragment添加到一個已存在的ViewGroup.
當activity運行的任何時候,都可以將fragment添加到activitylayout.只需簡單的指定一個需要放置fragment的ViewGroup.為了在你的activity中操作fragment事務(例如添加,移除,或代替一個fragment),必須使用來自FragmentTransaction的API.

可以按如下方法,從你的Activity取得一個FragmentTransaction的實例:

 

FragmentManager fragmentManager = getFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

 

然後你可以使用add()方法添加一個fragment,指定要添加的fragment,和要插入的view.

 

ExampleFragment fragment = new ExampleFragment();

fragmentTransaction.add(R.id.fragment_container, fragment);

fragmentTransaction.commit();

 

add()的第一個參數是fragment要放入的ViewGroup,由resource ID指定,第二個參數是需要添加的fragment.一旦用FragmentTransaction做了改變,為了使改變生效,必須調用commit().

 

添加一個無UI的fragment

之前的例子展示了對UI的支持,如何將一個fragment添加到activity.然而,也可以使用fragment來為activity提供後台行為而不用展現額外的UI.

要添加一個無UI的fragment,需要從activity使用add(Fragment, String)來添加fragment (為fragment提供一個唯一的字符串"tag",而不是一個view ID).這麼做添加了fragment,但因為它沒有關聯到一個activity layout中的一個view,所以不會接收到onCreateView()調用.因此不必實現此方法.

為fragment提供一個字符串tag並不是專門針對無UI的fragment的–也可以提供字符串tag給有UI的fragment –但是如果fragment沒有UI,那麼這個tag是僅有的標識它的途徑.如果隨後你想從activity獲取這個fragment,需要使用findFragmentByTag().

管理Fragment

 


要在activity中管理fragment,需要使用FragmentManager.通過調用activity的getFragmentManager()取得它的實例.

可以通過FragmentManager做一些事情,包括:

 

使用findFragmentById() (用於在activitylayout中提供一個UI的fragment)或findFragmentByTag()(適用於有或沒有UI的fragment)獲取activity中存在的fragment

將fragment從後台堆棧中彈出,使用popBackStack() (模擬用戶按下BACK命令).

使用addOnBackStackChangeListener()注冊一個監聽後台堆棧變化的listener.


處理Fragment事務

 


關於在activity中使用fragment的很強的一個特性是:根據用戶的交互情況,對fragment進行添加,移除,替換,以及執行其他動作.提 交給activity的每一套變化被稱為一個事務,可以使用在FragmentTransaction中的API處理.我們也可以保存每一個事務到一個activity管理的backstack,允許用戶經由fragment的變化往回導航(類似於通過activity往後導航).

從FragmentManager獲得一個FragmentTransaction的實例:

 

FragmentManager fragmentManager = getFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

 

每一個事務都是同時要執行的一套變化.可以在一個給定的事務中設置你想執行的所有變化,使用諸如add(), remove(),和replace().然後,要給activity應用事務,必須調用commit().

  在調用commit()之前,你可能想調用addToBackStack(),將事務添加到一個fragment事務的backstack.這個back stack由activity管理,並允許用戶通過按下BACK按鍵返回到前一個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替換了當前layout容器中的由R.id.fragment_container標識的fragment.通過調用addToBackStack(), replace事務被保存到back stack,因此用戶可以回退事務,並通過按下BACK按鍵帶回前一個fragment.

如果添加多個變化到事務(例如add()或remove())並調用addToBackStack(),然後在你調用commit()之前的所有應用的變化會被作為一個單個事務添加到後台堆棧, BACK按鍵會將它們一起回退.

添加變化到FragmentTransaction的順序不重要,除以下例外:

 

必須最後調用commit().

如果添加多個fragment到同一個容器,那麼添加的順序決定了它們在view hierarchy中顯示的順序.

當執行一個移除fragment的事務時,如果沒有調用addToBackStack(),那麼當事務提交後,那個fragment會被銷毀,並且用戶不能導航回到它.有鑒於此,當移除一個fragment時,如果調用了addToBackStack(),那麼fragment會被停止,如果用戶導航回來,它將會被恢復.

提示:對於每一個fragment事務,你可以應用一個事務動畫,通過在提交事務之前調用setTransition()實現.

調用commit()並不立即執行事務.恰恰相反,它將事務安排排期,一旦准備好,就在activity的UI線程上運行(主線程).如果有必要,無論如何,你可以從你的UI線程調用executePendingTransactions()來立即執行由commit()提交的事務.但這麼做通常不必要,除非事務是其他線程中的job的一個從屬.

警告:你只能在activity保存它的狀態(當用戶離開activity)之前使用commit()提交事務.

如果你試圖在那個點之後提交,會拋出一個異常.這是因為如果activity需要被恢復,提交之後的狀態可能會丟失.對於你覺得可以丟失提交的狀況,使用commitAllowingStateLoss().

與Activity通信

 


盡管Fragment被實現為一個獨立於Activity的對象,並且可以在多個activity中使用,但一個給定的fragment實例是直接綁定到 包含它的activity的.特別的,fragment可以使用getActivity()訪問Activity實例,並且容易地執行比如在activitylayout中查找一個view的任務.

 

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

 

同樣地,activity可以通過從FragmentManager獲得一個到Fragment的引用來調用fragment中的方法,使用findFragmentById()或findFragmentByTag().

 

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

 

 

 

為Activity創建事件回調方法

 

在一些情況下,你可能需要一個fragment與activity分享事件.一個好的方法是在fragment中定義一個回調的interface,並要求宿主activity實現它.當activity通過interface接收到一個回調,必要時它可以和在layout中的其他fragment分享信息.

例如,如果一個新的應用在activity中有2個fragment –一個用來顯示文章列表(framgent A),另一個顯示文章內容(fragment B) –然後framgent A必須告訴activity何時一個list item被選中,然後它可以告訴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);

 

}

...

}

 

然後fragment的宿主activity實現OnArticleSelectedListener接口,並覆寫onArticleSelected()來通知fragment B,從fragment A到來的事件.為了確保宿主activity實現這個接口, fragment A的onAttach()回調方法(當添加fragment到activity時由系統調用)通過將作為參數傳入onAttach()的Activity做類型轉換來實例化一個OnArticleSelectedListener實例.

 

 

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");

}

}

 

...

 

}

 

如果activity沒有實現接口, fragment會拋出ClassCastException異常.正常情形下, mListener成員會保持一個到activity的OnArticleSelectedListener實現的引用,因此fragment A可以通過調用在OnArticleSelectedListener接口中定義的方法分享事件給activity.例如,如果fragment A是一個ListFragment的子類,每次用戶點擊一個列表項,系統調用在fragment中的onListItemClick(),然後後者調用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參數是被點擊的項的行ID, activity(或其他fragment)用來從應用的ContentProvider獲取文章.

 

 

 

添加項目到Action Bar

 

你的fragment可以通過實現onCreateOptionMenu()提供菜單項給activity的選項菜單(以此類推, Action Bar也一樣).為了使這個方法接收調用,無論如何,你必須在onCreate()期間調用setHasOptionsMenu()來指出fragment願意添加item到選項菜單(否則, fragment將接收不到對onCreateOptionsMenu()的調用).

隨後從fragment添加到Option菜單的任何項,都會被追加到現有菜單項的後面.當一個菜單項被選擇, fragment也會接收到對onOptionsItemSelected()的回調.也可以在你的fragment layout中通過調用registerForContextMenu()注冊一個view來提供一個環境菜單.當用戶打開環境菜單, fragment接收到一個對onCreateContextMenu()的調用.當用戶選擇一個項目, fragment接收到一個對onContextItemSelected()的調用.

注意:盡管你的fragment會接收到它所添加的每一個菜單項被選擇後的回調,但實際上當用戶選擇一個菜單項時, activity會首先接收到對應的回調.如果activity的on-item-selected回調函數實現並沒有處理被選中的項目,然後事件才會被傳遞到fragment的回調.

這個規則適用於選項菜單和環境菜單.

 

 

 

管理fragment的生命周期,大多數地方和管理activity生命周期很像.和activity一樣, fragment可以處於3種狀態:
Resumed
在運行中的activity中fragment可見.
Paused

另一個activity處於前台並擁有焦點,但是這個fragment所在的activity仍然可見(前台activity局部透明或者沒有覆蓋整個屏幕).

Stopped

要麼是宿主activity已經被停止,要麼是fragment從activity被移除但被添加到後台堆棧中.

停止狀態的fragment仍然活著(所有狀態和成員信息被系統保持著).然而,它對用戶不再可見,並且如果activity被干掉,他也會被干掉.

仍然和activity一樣,你可以使用Bundle保持fragment的狀態,萬一activity的進程被干掉,並且當activity被重新創建的時候,你需要恢復fragment的狀態時就可以用到.你可以在fragment的onSaveInstanceState()期間保存狀態,並可以在onCreate(), onCreateView()或onActivityCreated()期間恢復它.

生命周期方面activity和fragment之間最重要的區別是各自如何在它的後台堆棧中儲存.默認地, activity在停止後,它會被放到一個由系統管理的用於保存activity的後台堆棧.(因此用戶可以使用BACK按鍵導航回退到它).

然而,僅當你在一個事務期間移除fragment時,顯式調用addToBackStack()請求保存實例時,才被放到一個由宿主activity管理的後台堆棧.

另外,管理fragment的生命周期和管理activity生命周期非常類似.因此, "managing the activity lifecycle"中的相同實踐也同樣適用於fragment.你需要理解的是,activity的生命如何影響fragment的生命.

與activity生命周期的協調工作
fragment所生存的activity的生命周期,直接影響fragment的生命周期,每一個activity的生命周期的回調行為都會引起每一個fragment中類似的回調.

例如,當activity接收到onPause()時,activity中的每一個fragment都會接收到onPause().

Fragment有一些額外的生命周期回調方法,那些是處理與activity的唯一的交互,為了執行例如創建和銷毀fragment的UI的動作.這些額外的回調方法是:

 

onAttach()
當fragment被綁定到activity時被調用(Activity會被傳入.).

onCreateView()
創建和fragment關聯的view hierarchy時調用.

onActivityCreated()
當activity的onCreate()方法返回時被調用.

onDestroyView()
當和fragment關聯的view hierarchy正在被移除時調用.

onDetach()
當fragment從activity解除關聯時被調用.


fragment生命周期的流程,以及宿主activity對它的影響,在圖3中顯示.在這個圖中,可以看到activity依次的每個狀態是如何決定fragment可能接收到的回調方法.例如,當activity接收到它的onCreate(), activity中的fragment接收到最多是onActivityCreated().

一旦activity到達了resumed狀態,你可以自由地在activity添加和移除fragment.因此,僅當activity處於resumed狀態時, fragment的生命周期才可以獨立變化.

無論如何,當activity離開resumed狀態,fragment再次被activity的推入它自己的生命周期過程.

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved