Android在3.0中引入了fragments的概念,主要目的是用在大屏幕設備上--例如平板電腦上,支持更加動態和靈活的UI設計。平板電腦的屏幕要比手機的大得多,有更多的空間來放更多的UI組件,并且這些組件之間會產生更多的交互。Fragment允許這樣的一種設計,而不需要你親自來管理 viewhierarchy的復雜變化。 通過將activity的布局分散到fragment中, 你可以在運行時修改activity的外觀,并在由activity管理的back stack中保存那些變化.(http://developer.android.com/guide/topics/fundamentals/fragments.html)
例如, 一個新聞應用可以在屏幕左側使用一個fragment來展示一個文章的列表,然后在屏幕右側使用另一個fragment來展示一篇文章--2個fragment并排顯示在相同的一個activity中,并且每一個fragment擁有它自己的一套生命周期回調方法,并且處理它們自己的用戶輸入事件。 因此, 取代使用一個activity來選擇一篇文章而另一個activity來閱讀文章的方式,用戶可以在同一個activity中選擇一篇文章并且閱讀, 如圖所示

fragment在你的應用中應當是一個模塊化和可重用的組件.即,因為fragment定義了它自己的布局, 以及通過使用它自己的生命周期回調方法定義了它自己的行為,你可以將fragment包含到多個activity中. 這點特別重要, 因為這允許你將你的用戶體驗適配到不同的屏幕尺寸.舉個例子,你可能會僅當在屏幕尺寸足夠大時,在一個activity中包含多個fragment,并且,當不屬于這種情況時,會啟動另一個單獨的,使用不同fragment的activity. 繼續之前那個新聞的例子 -- 當運行在一個特別大的屏幕時(例如平板電腦),應用可以在Activity A中嵌入2個fragment。然而,在一個正常尺寸的屏幕(例如手機)上,沒有足夠的空間同時供2個fragment用, 因此, Activity A會僅包含文章列表的fragment, 而當用戶選擇一篇文章時, 它會啟動ActivityB,它包含閱讀文章的fragment.因此, 應用可以同時支持上圖中的2種設計模式。
創建Fragment 要創建一個fragment, 必須創建一個 Fragment 的子類 (或者繼承自一個已存在的它的子類). Fragment類的代碼看起來很像 Activity 。它包含了和activity類似的回調方法, 例如onCreate()、 onStart()、onPause()以及 onStop()。事實上, 如果你準備將一個現成的Android應用轉換到使用fragment,可能只需簡單的將代碼從你的activity的回調方法分別移動到你的fragment的回調方法即可。通常, 應當至少實現如下的生命周期方法:
其生命周期圖如下:
大多數應用應當為每一個fragment實現至少這3個方法,但是還有一些其他回調方法你也應當用來去處理fragment生命周期的各種階段.全部的生命周期回調方法將會在后面章節 Handlingthe Fragment Lifecycle 中討論. 除了繼承基類 Fragment , 還有一些子類你可能會繼承:
fragment通常用來作為一個activity的用戶界面的一部分,并將它的layout提供給activity.為了給一個fragment提供一 個layout,你必須實現 onCreateView()回調方法, 當到了fragment繪制它自己的layout的時候,Android系統調用它.你的此方法的實現代碼必須返回一個你的fragment的 layout的根view. 注意:如果你的fragment是ListFragment的子類,它的默認實現是返回從onCreateView()返回一個ListView,所以一般情況下不必實現它. 從onCreateView()返回的View, 也可以從一個layout的xml資源文件中讀取并生成. 為了幫助你這么做, onCreateView() 提供了一個LayoutInflater 對象. 舉個例子, 這里有一個Fragment的子類, 從文件 example_fragment.xml 加載了一個layout:
1 public static class ExampleFragment extends Fragment { 2 @Override 3 public View onCreateView(LayoutInflater inflater, ViewGroup container, 4 Bundle savedInstanceState) { 5 // Inflate the layout for this fragment 6 return inflater.inflate(R.layout.example_fragment, container, false); 7 } 8 } 傳入onCreateView()的container參數是你的fragmentlayout將被插入的父ViewGroup(來自activity的layout) savedInstanceState 參數是一個Bundle, 如果fragment是被恢復的,它提供關于fragment的之前的實例的數據,inflate() 方法有3個參數:
通常地, fragment為宿主activity提供UI的一部分, 被作為activity的整個viewhierarchy的一部分被嵌入. 有2種方法你可以添加一個fragment到activity layout:在activity的layout文件中聲明fragment 在這種情況下,你可以像為View一樣, 為fragment指定layout屬性.例子是一個有2個fragment的activity的layout:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="horizontal" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent"> 6 <fragment android:name="com.example.news.ArticleListFragment" 7 android:id="@+id/list" 8 android:layout_weight="1" 9 android:layout_width="0dp" 10 android:layout_height="match_parent" /> 11 <fragment android:name="com.example.news.ArticleReaderFragment" 12 android:id="@+id/viewer" 13 android:layout_weight="2" 14 android:layout_width="0dp" 15 android:layout_height="match_parent" /> 16 </LinearLayout><fragment> 中的 android:name屬性指定了在layout中實例化的Fragment類. 當系統創建這個activity layout時,它實例化每一個在layout中指定的fragment,并調用每一個上的onCreateView()方法,來獲取每一個 fragment的layout.系統將從fragment返回的 View直接插入到<fragment>元素所在的地方. 注意:每一個fragment都需要一個唯一的標識,如果activity重啟,系統可以用來恢復fragment(并且你也可以用來捕獲fragment來處理事務,例如移除它.)
有3種方法來為一個fragment提供一個標識:
當activity運行的任何時候, 都可以將fragment添加到activity layout.只需簡單的指定一個需要放置fragment的ViewGroup.為了在你的 activity中操作fragment事務(例如添加,移除,或代替一個fragment),必須使用來自FragmentTransaction 的API. 可以按如下方法,從你的Activity取得一個 FragmentTransaction 的實例:
1 FragmentManager fragmentManager =getFragmentManager();
2 FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
然后你可以使用 add() 方法添加一個fragment, 指定要添加的fragment和要插入的view.
1 ExampleFragment fragment = newExampleFragment();
2 fragmentTransaction.add(R.id.fragment_container,fragment);
3 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做一些事情, 包括:
關于在activity中使用fragment的很強的一個特性是:根據用戶的交互情況,對fragment進行添加,移除,替換,以及執行其他動作.提交給activity的每一套變化被稱為一個事務,可以使用在FragmentTransaction中的 API 處理.我們也可以保存每一個事務到一個activity管理的backstack,允許用戶經由fragment的變化往回導航(類似于通過 activity往后導航). 從 FragmentManager 獲得一個FragmentTransaction實例 :
[java]view plaincopy1 FragmentManager fragmentManager =getFragmentManager(); 2 FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction(); 3
每一個事務都是同時要執行的一套變化.可以在一個給定的事務中設置你想執行的所有變化,使用諸如 add()、remove()和 replace().然后, 要給activity應用事務, 必須調用 commit(). 在調用commit()之前, 你可能想調用 addToBackStack(),將事務添加到一個fragment事務的backstack. 這個back stack由activity管理, 并允許用戶通過按下 BACK按鍵返回到前一個fragment狀態. 舉個例子, 這里是如何將一個fragment替換為另一個, 并在后臺堆棧中保留之前的狀態:
1 // Create new fragment and transaction 2 Fragment newFragment = newExampleFragment(); 3 FragmentTransaction transaction =getFragmentManager().beginTransaction(); 4 // Replace whatever is in thefragment_container view with this fragment, 5 // and add the transaction to the backstack 6 transaction.replace(R.id.fragment_container,newFragment); 7 transaction.addToBackStack(null); 8 // Commit the transaction 9 transaction.commit();
在這個例子中,newFragment替換了當前layout容器中的由R.id.fragment_container標識的fragment.通過調用 addToBackStack(), replace事務被保存到back stack,因此用戶可以回退事務,并通過按下BACK按鍵帶回前一個fragment. 如果添加多個變化到事務(例如add()或remove())并調用addToBackStack(),然后在你調用commit()之前的所有應用的變化會被作為一個單個事務添加到后臺堆棧, BACK按鍵會將它們一起回退. 添加變化到 FragmentTransaction的順序不重要, 除以下例外:
盡管Fragment被實現為一個獨立于Activity的對象,并且可以在多個activity中使用,但一個給定的fragment實例是直接綁定到包含它的activity的. 特別的,fragment可以使用 getActivity() 訪問Activity實例, 并且容易地執行比如在activity layout中查找一個view的任務.
1 View listView =getActivity().findViewById(R.id.list);<span style="font-family:System;"> </span>
同樣地,activity可以通過從FragmentManager獲得一個到Fragment的引用來調用fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().
1 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被選中,然后它可以告訴fragmentB去顯示文章. 在這個例子中, OnArticleSelectedListener 接口在fragment A中聲明:
1 public static class FragmentA extends ListFragment { 2 ... 3 // Container Activity must implement this interface 4 public interface OnArticleSelectedListener { 5 public void onArticleSelected(Uri articleUri); 6 7 } 8 ... 9 } 然后fragment的宿主activity實現 OnArticleSelectedListener 接口, 并覆寫 onArticleSelected() 來通知fragment B,從fragment A到來的事件.為了確保宿主activity實現這個接口, fragment A的 onAttach() 回調方法(當添加fragment到activity時由系統調用) 通過將作為參數傳入onAttach()的Activity做類型轉換來實例化一個OnArticleSelectedListener實例.
1 public static class FragmentA extends ListFragment { 2 OnArticleSelectedListener mListener; 3 ... 4 @Override 5 public void onAttach(Activity activity) { 6 super.onAttach(activity); 7 try { 8 mListener = (OnArticleSelectedListener) activity; 9 } catch (ClassCastException e) { 10 throw new ClassCastException(activity.toString() + " must implementOnArticleSelectedListener"); 11 } 12 } 13 14 ... 15 16 } 如果activity沒有實現接口, fragment會拋出 ClassCastException 異常. 正常情形下,mListener成員會保持一個到activity的OnArticleSelectedListener實現的引用, 因此fragment A可以通過調用在OnArticleSelectedListener接口中定義的方法分享事件給activity.例如, 如果fragment A是一個 ListFragment的子類, 每次用戶點擊一個列表項, 系統調用在fragment中的onListItemClick(),然后后者調用 onArticleSelected() 來分配事件給activity.
1 public static class FragmentA extends ListFragment { 2 OnArticleSelectedListener mListener; 3 ... 4 @Override 5 public void onListItemClick(ListView l, View v, int position, long id) { 6 // Append the clicked item's row ID with the content provider Uri 7 Uri noteUri =ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id); 8 // Send the event and Uri to the host activity 9 mListener.onArticleSelected(noteUri); 10 } 11 12 ... 13 14 } 傳給 onListItemClick() 的 id 參數是被點擊的項的行ID, activity(或其他fragment)用來從應用的 ContentProvider 獲取文章.添加項目到ActionBar你的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的生命周期管理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 activitylifecycle"中的相同實踐也同樣適用于fragment. 你需要理解的是,activity的生命如何影響fragment的生命.
fragment生命周期的流程, 以及宿主activity對它的影響,在圖3中顯示.在這個圖中,可以看到activity依次的每個狀態是如何決定fragment可能接收到的回調方法.例如, 當activity接收到它的onCreate(),activity中的fragment接收到最多是onActivityCreated(). 一旦activity到達了resumed狀態, 你可以自由地在activity添加和移除fragment.因此,僅當activity處于resumed狀態時, fragment的生命周期才可以獨立變化. 無論如何, 當activity離開resumed狀態,fragment再次被activity的推入它自己的生命周期過程.
新聞熱點
疑難解答