Fragment要點
- Fragment作為Activity界面的一部分組成出現(xiàn)
- 可以在一個Activity中同時出現(xiàn)多個Fragment,并且,一個Fragment亦可在多個Activity中使用。
- 在Activity運行過程中,可以添加、移除或者替換Fragment(add()、remove()、replace())
- Fragment可以響應(yīng)自己的輸入事件,并且有自己的生命周期,當(dāng)然,它們的生命周期直接被其所屬的宿主activity的生命周期影響。
設(shè)計哲學(xué)
Android在3.0中引入了fragments的概念,主要目的是用在大屏幕設(shè)備上--例如平板電腦上,支持更加動態(tài)和靈活的UI設(shè)計。平板電腦的屏幕要比手機的大得多,有更多的空間來放更多的UI組件,并且這些組件之間會產(chǎn)生更多的交互。Fragment允許這樣的一種設(shè)計,而不需要你親自來管理 viewhierarchy的復(fù)雜變化。 通過將activity的布局分散到fragment中, 你可以在運行時修改activity的外觀,并在由activity管理的back stack中保存那些變化.( http://developer.android.com/guide/topics/fundamentals/fragments.html )
例如, 一個新聞應(yīng)用可以在屏幕左側(cè)使用一個fragment來展示一個文章的列表,然后在屏幕右側(cè)使用另一個fragment來展示一篇文章--2個fragment并排顯示在相同的一個activity中,并且每一個fragment擁有它自己的一套生命周期回調(diào)方法,并且處理它們自己的用戶輸入事件。 因此, 取代使用一個activity來選擇一篇文章而另一個activity來閱讀文章的方式,用戶可以在同一個activity中選擇一篇文章并且閱讀, 如圖所示:
fragment在你的應(yīng)用中應(yīng)當(dāng)是一個模塊化和可重用的組件.即,因為fragment定義了它自己的布局, 以及通過使用它自己的生命周期回調(diào)方法定義了它自己的行為,你可以將fragment包含到多個activity中. 這點特別重要, 因為這允許你將你的用戶體驗適配到不同的屏幕尺寸.舉個例子,你可能會僅當(dāng)在屏幕尺寸足夠大時,在一個activity中包含多個fragment,并且,當(dāng)不屬于這種情況時,會啟動另一個單獨的,使用不同fragment的activity.
繼續(xù)之前那個新聞的例子 -- 當(dāng)運行在一個特別大的屏幕時(例如平板電腦),應(yīng)用可以在Activity A中嵌入2個fragment。然而,在一個正常尺寸的屏幕(例如手機)上,沒有足夠的空間同時供2個fragment用, 因此, Activity A會僅包含文章列表的fragment, 而當(dāng)用戶選擇一篇文章時, 它會啟動ActivityB,它包含閱讀文章的fragment.因此, 應(yīng)用可以同時支持上圖中的2種設(shè)計模式。
創(chuàng)建Fragment
通常, 應(yīng)當(dāng)至少實現(xiàn)如下的生命周期方法:
-
onCreate()
當(dāng)創(chuàng)建fragment時, 系統(tǒng)調(diào)用該方法.
在實現(xiàn)代碼中,應(yīng)當(dāng)初始化想要在fragment中保持的必要組件, 當(dāng)fragment被暫停或者停止后可以恢復(fù). -
onCreateView()
fragment第一次繪制它的用戶界面的時候, 系統(tǒng)會調(diào)用此方法. 為了繪制fragment的UI,此方法必須返回一個View, 這個view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null. -
onPause()
用戶將要離開fragment時,系統(tǒng)調(diào)用這個方法作為第一個指示(然而它不總是意味著fragment將被銷毀.) 在當(dāng)前用戶會話結(jié)束之前,通常應(yīng)當(dāng)在這里提交任何應(yīng)該持久化的變化(因為用戶有可能不會返回).
其生命周期圖如下:
大多數(shù)應(yīng)用應(yīng)當(dāng)為每一個fragment實現(xiàn)至少這3個方法,但是還有一些其他回調(diào)方法你也應(yīng)當(dāng)用來去處理fragment生命周期的各種階段.全部的生命周期回調(diào)方法將會在后面章節(jié) Handlingthe Fragment Lifecycle 中討論.
除了繼承基類 Fragment , 還有一些子類你可能會繼承:
-
DialogFragment
顯示一個浮動的對話框.
用這個類來創(chuàng)建一個對話框,是使用在Activity類的對話框工具方法之外的一個好的選擇,
因為你可以將一個fragment對話框合并到activity管理的fragment back stack中,允許用戶返回到一個之前曾被摒棄的fragment. -
ListFragment
顯示一個由一個adapter(例如 SimpleCursorAdapter)管理的項目的列表, 類似于ListActivity.
它提供一些方法來管理一個list view, 例如 onListItemClick()回調(diào)來處理點擊事件. -
PreferenceFragment
顯示一個 Preference對象的層次結(jié)構(gòu)的列表, 類似于PreferenceActivity.
這在為你的應(yīng)用創(chuàng)建一個"設(shè)置"activity時有用處.
添加一個用戶界面
fragment通常用來作為一個activity的用戶界面的一部分,并將它的layout提供給activity.為了給一個fragment提供一 個layout,你必須實現(xiàn) onCreateView()回調(diào)方法, 當(dāng)?shù)搅薴ragment繪制它自己的layout的時候,Android系統(tǒng)調(diào)用它.你的此方法的實現(xiàn)代碼必須返回一個你的fragment的 layout的根view.
注意
:
如果你的fragment是ListFragment的子類,它的默認(rèn)實現(xiàn)是返回從onCreateView()返回一個ListView,所以一般情況下不必實現(xiàn)它.
從onCreateView()返回的View, 也可以從一個layout的xml資源文件中讀取并生成. 為了幫助你這么做, onCreateView() 提供了一個LayoutInflater 對象.
舉個例子, 這里有一個Fragment的子類, 從文件 example_fragment.xml 加載了一個layout:
- public static class ExampleFragment extends Fragment{
- @Override
- public ViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
- BundlesavedInstanceState){
- //Inflatethelayoutforthisfragment
- return inflater.inflate(R.layout.example_fragment,container, false );
- }
- }
傳入onCreateView()的container參數(shù)是你的fragmentlayout將被插入的父ViewGroup(來自activity的layout) savedInstanceState 參數(shù)是一個Bundle, 如果fragment是被恢復(fù)的,它提供關(guān)于fragment的之前的實例的數(shù)據(jù),
inflate() 方法有3個參數(shù):
- 想要加載的layout的resource ID.
-
加載的layout的父ViewGroup.
傳入container是很重要的, 目的是為了讓系統(tǒng)接受所要加載的layout的根view的layout參數(shù),
由它將掛靠的父view指定. -
布爾值指示在加載期間, 展開的layout是否應(yīng)當(dāng)附著到ViewGroup (第二個參數(shù)).
(在這個例子中, 指定了false, 因為系統(tǒng)已經(jīng)把展開的layout插入到container –傳入true會在最后的layout中創(chuàng)建一個多余的view group.)
將fragment添加到activity
通常地, fragment為宿主activity提供UI的一部分, 被作為activity的整個viewhierarchy的一部分被嵌入. 有2種方法你可以添加一個fragment到activity layout:在activity的layout文件中聲明fragment
在這種情況下,你可以像為View一樣, 為fragment指定layout屬性.例子是一個有2個fragment的activity的layout:
- <? 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屬性指定了在layout中實例化的Fragment類.
當(dāng)系統(tǒng)創(chuàng)建這個activity layout時,它實例化每一個在layout中指定的fragment,并調(diào)用每一個上的onCreateView()方法,來獲取每一個 fragment的layout.系統(tǒng)將從fragment返回的 View直接插入到<fragment>元素所在的地方.
注意 : 每一個fragment都需要一個唯一的標(biāo)識,如果activity重啟,系統(tǒng)可以用來恢復(fù)fragment(并且你也可以用來捕獲fragment來處理事務(wù),例如移除它.)
有3種方法來為一個fragment提供一個標(biāo)識:
- 為 android:id 屬性提供一個唯一ID.
- 為 android:tag 屬性提供一個唯一字符串.
- 如果以上2個你都沒有提供, 系統(tǒng)使用容器view的ID.
撰寫代碼將fragment添加到一個已存在的ViewGroup.
當(dāng)activity運行的任何時候, 都可以將fragment添加到activity layout.只需簡單的指定一個需要放置fragment的ViewGroup.為了在你的 activity中操作fragment事務(wù)(例如添加,移除,或代替一個fragment),必須使用來自FragmentTransaction 的API.可以按如下方法,從你的Activity取得一個 FragmentTransaction 的實例:
- FragmentManagerfragmentManager=getFragmentManager();
- FragmentTransactionfragmentTransaction=fragmentManager.beginTransaction();
然后你可以使用 add() 方法添加一個fragment, 指定要添加的fragment和要插入的view.
- ExampleFragmentfragment=newExampleFragment();
- fragmentTransaction.add(R.id.fragment_container,fragment);
- fragmentTransaction.commit();
add()的第一個參數(shù)是fragment要放入的ViewGroup, 由resource ID指定,第二個參數(shù)是需要添加的fragment.一旦用FragmentTransaction做了改變,為了使改變生效,必須調(diào)用commit().
添加一個無UI的fragment
之前的例子展示了對UI的支持, 如何將一個fragment添加到activity.然而,也可以使用fragment來為activity提供后臺行為而不用展現(xiàn)額外的UI.要添加一個無UI的fragment, 需要從activity使用 add(Fragment, String)來添加fragment (為fragment提供一個唯一的字符串"tag", 而不是一個view ID).這么做添加了fragment,但因為它沒有關(guān)聯(lián)到一個activity layout中的一個view, 所以不會接收到onCreateView()調(diào)用.因此不必實現(xiàn)此方法.
為fragment提供一個字符串tag并不是專門針對無UI的fragment的–也可以提供字符串tag給有UI的fragment–但是如果fragment沒有UI,那么這個tag是僅有的標(biāo)識它的途徑.如果隨后你想從activity獲取這個fragment, 需要使用 findFragmentByTag().
管理Fragment
要在activity中管理fragment,需要使用FragmentManager. 通過調(diào)用activity的getFragmentManager()取得它的實例.
可以通過FragmentManager做一些事情, 包括:
- 使用findFragmentById()(用于在activity layout中提供一個UI的fragment)或findFragmentByTag()(適用于有或沒有UI的fragment)獲取activity中存在的fragment
- 將fragment從后臺堆棧中彈出, 使用 popBackStack() (模擬用戶按下BACK 命令).
- 使用addOnBackStackChangeListener()注冊一個監(jiān)聽后臺堆棧變化的listener.
處理Fragment事務(wù)
關(guān)于在activity中使用fragment的很強的一個特性是:根據(jù)用戶的交互情況,對fragment進行添加,移除,替換,以及執(zhí)行其他動作.提交給activity的每一套變化被稱為一個事務(wù),可以使用在FragmentTransaction中的 API 處理.我們也可以保存每一個事務(wù)到一個activity管理的backstack,允許用戶經(jīng)由fragment的變化往回導(dǎo)航(類似于通過 activity往后導(dǎo)航).
從 FragmentManager 獲得一個FragmentTransaction實例 :
- FragmentManagerfragmentManager=getFragmentManager();
- FragmentTransactionfragmentTransaction=fragmentManager.beginTransaction();
每一個事務(wù)都是同時要執(zhí)行的一套變化.可以在一個給定的事務(wù)中設(shè)置你想執(zhí)行的所有變化,使用諸如 add()、remove()和 replace().然后, 要給activity應(yīng)用事務(wù), 必須調(diào)用 commit().
在調(diào)用commit()之前, 你可能想調(diào)用 addToBackStack(),將事務(wù)添加到一個fragment事務(wù)的backstack. 這個back stack由activity管理, 并允許用戶通過按下 BACK按鍵返回到前一個fragment狀態(tài).
舉個例子, 這里是如何將一個fragment替換為另一個, 并在后臺堆棧中保留之前的狀態(tài):
- //Createnewfragmentandtransaction
- FragmentnewFragment=newExampleFragment();
- FragmentTransactiontransaction=getFragmentManager().beginTransaction();
- //Replacewhateverisinthefragment_containerviewwiththisfragment,
- //andaddthetransactiontothebackstack
- transaction.replace(R.id.fragment_container,newFragment);
- transaction.addToBackStack( null );
- //Committhetransaction
- transaction.commit();
在這個例子中,newFragment替換了當(dāng)前l(fā)ayout容器中的由R.id.fragment_container標(biāo)識的fragment.通過調(diào)用 addToBackStack(), replace事務(wù)被保存到back stack,因此用戶可以回退事務(wù),并通過按下BACK按鍵帶回前一個fragment.
如果添加多個變化到事務(wù)(例如add()或remove())并調(diào)用addToBackStack(),然后在你調(diào)用commit()之前的所有應(yīng)用的變化會被作為一個單個事務(wù)添加到后臺堆棧, BACK按鍵會將它們一起回退.
添加變化到 FragmentTransaction的順序不重要, 除以下例外:
- 必須最后調(diào)用 commit().
- 如果添加多個fragment到同一個容器, 那么添加的順序決定了它們在view hierarchy中顯示的順序.
提示: 對于每一個fragment事務(wù), 你可以應(yīng)用一個事務(wù)動畫,通過在提交事務(wù)之前調(diào)用setTransition()實現(xiàn).
調(diào)用 commit() 并不立即執(zhí)行事務(wù).恰恰相反, 它將事務(wù)安排排期, 一旦準(zhǔn)備好,就在activity的UI線程上運行(主線程).如果有必要, 無論如何, 你可以從你的UI線程調(diào)用executePendingTransactions()來立即執(zhí)行由commit()提交的事務(wù). 但這么做通常不必要,除非事務(wù)是其他線程中的任務(wù)的一個從屬.
警告 :你只能在activity保存它的狀態(tài)(當(dāng)用戶離開activity)之前使用commit()提交事務(wù).
與Activity通信
盡管Fragment被實現(xiàn)為一個獨立于Activity的對象,并且可以在多個activity中使用,但一個給定的fragment實例是直接綁定到包含它的activity的. 特別的,fragment可以使用 getActivity() 訪問Activity實例, 并且容易地執(zhí)行比如在activity layout中查找一個view的任務(wù).
- ViewlistView=getActivity().findViewById(R.id.list);<spanstyle= "font-family:System;" ></span>
同樣地,activity可以通過從FragmentManager獲得一個到Fragment的引用來調(diào)用fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().
- ExampleFragmentfragment=(ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);
為Activity創(chuàng)建事件回調(diào)方法
在一些情況下, 你可能需要一個fragment與activity分享事件. 一個好的方法是在fragment中定義一個回調(diào)的interface, 并要求宿主activity實現(xiàn)它.當(dāng)activity通過interface接收到一個回調(diào), 必要時它可以和在layout中的其他fragment分享信息.
例如, 如果一個新的應(yīng)用在activity中有2個fragment – 一個用來顯示文章列表(framgent A), 另一個顯示文章內(nèi)容(fragment B) – 然后 framgent A必須告訴activity何時一個list item被選中,然后它可以告訴fragmentB去顯示文章.
在這個例子中, OnArticleSelectedListener 接口在fragment A中聲明:
- public static class FragmentA extends ListFragment{
- ...
- //ContainerActivitymustimplementthisinterface
- public interface OnArticleSelectedListener{
- public void onArticleSelected(UriarticleUri);
- }
- ...
- }
然后fragment的宿主activity實現(xiàn) OnArticleSelectedListener 接口, 并覆寫 onArticleSelected() 來通知fragment B,從fragment A到來的事件.為了確保宿主activity實現(xiàn)這個接口, fragment A的 onAttach() 回調(diào)方法(當(dāng)添加fragment到activity時由系統(tǒng)調(diào)用) 通過將作為參數(shù)傳入onAttach()的Activity做類型轉(zhuǎn)換來實例化一個OnArticleSelectedListener實例.
- public static class FragmentA extends ListFragment{
- OnArticleSelectedListenermListener;
- ...
- @Override
- public void onAttach(Activityactivity){
- super .onAttach(activity);
- try {
- mListener=(OnArticleSelectedListener)activity;
- } catch (ClassCastExceptione){
- throw new ClassCastException(activity.toString()+ "mustimplementOnArticleSelectedListener" );
- }
- }
- ...
- }
如果activity沒有實現(xiàn)接口, fragment會拋出 ClassCastException 異常. 正常情形下,mListener成員會保持一個到activity的OnArticleSelectedListener實現(xiàn)的引用, 因此fragment A可以通過調(diào)用在OnArticleSelectedListener接口中定義的方法分享事件給activity.例如, 如果fragment A是一個 ListFragment的子類, 每次用戶點擊一個列表項, 系統(tǒng)調(diào)用在fragment中的onListItemClick(),然后后者調(diào)用 onArticleSelected() 來分配事件給activity.
- public static class FragmentA extends ListFragment{
- OnArticleSelectedListenermListener;
- ...
- @Override
- public void onListItemClick(ListViewl,Viewv, int position, long id){
- //Appendtheclickeditem'srowIDwiththecontentproviderUri
- UrinoteUri=ContentUris.withAppendedId(ArticleColumns.CONTENT_URI,id);
- //SendtheeventandUritothehostactivity
- mListener.onArticleSelected(noteUri);
- }
- ...
- }
傳給 onListItemClick() 的 id 參數(shù)是被點擊的項的行ID, activity(或其他fragment)用來從應(yīng)用的 ContentProvider 獲取文章.
添加項目到ActionBar
你的fragment可以通過實現(xiàn) onCreateOptionMenu() 提供菜單項給activity的選項菜單(以此類推, Action Bar也一樣).為了使這個方法接收調(diào)用,無論如何, 你必須在 onCreate() 期間調(diào)用 setHasOptionsMenu() 來指出fragment愿意添加item到選項菜單(否則, fragment將接收不到對 onCreateOptionsMenu()的調(diào)用).
隨后從fragment添加到Option菜單的任何項,都會被追加到現(xiàn)有菜單項的后面.當(dāng)一個菜單項被選擇, fragment也會接收到 對 onOptionsItemSelected() 的回調(diào).也可以在你的fragment layout中通過調(diào)用registerForContextMenu() 注冊一個view來提供一個環(huán)境菜單.當(dāng)用戶打開環(huán)境菜單, fragment接收到一個對 onCreateContextMenu() 的調(diào)用.當(dāng)用戶選擇一個項目, fragment接收到一個對onContextItemSelected() 的調(diào)用.
注意: 盡管你的fragment會接收到它所添加的每一個菜單項被選擇后的回調(diào),但實際上當(dāng)用戶選擇一個菜單項時, activity會首先接收到對應(yīng)的回調(diào).如果activity的on-item-selected回調(diào)函數(shù)實現(xiàn)并沒有處理被選中的項目, 然后事件才會被傳遞到fragment的回調(diào).
這個規(guī)則適用于選項菜單和環(huán)境菜單.
處理fragment的生命周期
管理fragment的生命周期, 大多數(shù)地方和管理activity生命周期很像.和activity一樣, fragment可以處于3種狀態(tài):
Resumed
在運行中的activity中fragment可見.
Paused
另一個activity處于前臺并擁有焦點, 但是這個fragment所在的activity仍然可見(前臺activity局部透明或者沒有覆蓋整 個屏幕).
Stopped
要么是宿主activity已經(jīng)被停止, 要么是fragment從activity被移除但被添加到后臺堆棧中.
停止?fàn)顟B(tài)的fragment仍然活著(所有狀態(tài)和成員信息被系統(tǒng)保持著). 然而, 它對用戶不再可見, 并且如果activity被干掉, 他也會被干掉.
其對應(yīng)關(guān)系圖如下:
和activity一樣, 你可以使用Bundle保持fragment的狀態(tài), 萬一activity的進程被干掉,并且當(dāng)activity被重新創(chuàng)建的時候, 你需要恢復(fù)fragment的狀態(tài)時就可以用到. 你可以在fragment的 onSaveInstanceState() 期間保存狀態(tài), 并可以在 onCreate(), onCreateView() 或 onActivityCreated() 期間恢復(fù)它.
生命周期方面activity和fragment之間最重要的區(qū)別是各自如何在它的后臺堆棧中儲存. 在默認(rèn)情況下, activity在停止后, 它會被放到一個由系統(tǒng)管理的用于保存activity的后臺堆棧.(因此用戶可以使用BACK按鍵導(dǎo)航回退到它).
然而, 僅當(dāng)你在一個事務(wù)期間移除fragment時,顯式調(diào)用addToBackStack()請求保存實例時,才被放到一個由宿主activity管理的后臺堆棧.
另外, 管理fragment的生命周期和管理activity生命周期非常類似.因此, "managing the activitylifecycle"中的相同實踐也同樣適用于fragment. 你需要理解的是,activity的生命如何影響fragment的生命.
與activity生命周期的協(xié)調(diào)工作
fragment所生存的activity的生命周期,直接影響fragment的生命周期,每一個activity的生命周期的回調(diào)行為都會引起每一個fragment中類似的回調(diào).例如,當(dāng)activity接收到onPause()時,activity中的每一個fragment都會接收到onPause().
Fragment 有一些額外的生命周期回調(diào)方法, 那些是處理與activity的唯一的交互,為了執(zhí)行例如創(chuàng)建和銷毀fragment的UI的動作. 這些額外的回調(diào)方法是:
-
onAttach()
當(dāng)fragment被綁定到activity時被調(diào)用(Activity會被傳入.). -
onCreateView()
創(chuàng)建和fragment關(guān)聯(lián)的view hierarchy時調(diào)用. -
onActivityCreated()
當(dāng)activity的onCreate()方法返回時被調(diào)用. -
onDestroyView()
當(dāng)和fragment關(guān)聯(lián)的view hierarchy正在被移除時調(diào)用. -
onDetach()
當(dāng)fragment從activity解除關(guān)聯(lián)時被調(diào)用.
fragment生命周期的流程, 以及宿主activity對它的影響,在圖3中顯示.在這個圖中,可以看到activity依次的每個狀態(tài)是如何決定fragment可能接收到的回調(diào)方法.例如, 當(dāng)activity接收到它的onCreate(),activity中的fragment接收到最多是onActivityCreated().
一旦activity到達了resumed狀態(tài), 你可以自由地在activity添加和移除fragment.因此,僅當(dāng)activity處于resumed狀態(tài)時, fragment的生命周期才可以獨立變化.
無論如何, 當(dāng)activity離開resumed狀態(tài),fragment再次被activity的推入它自己的生命周期過程.
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
