Android在3.0中引入了fragments的概念,主要目的是用在大屏幕設備上--好比平板電腦上,支持更加動態和靈活的UI設計。平板電腦的屏幕要比手機的大得多,有不少其它的空間來放不少其它的UI組件,並且這些組件之間會產生不少其它的交互。Fragment贊成這樣的一種設計,而不需要你親自來管理 viewhierarchy的複雜變化。 經過將activity的佈局分散到fragment中, 你可以在運行時改動activity的外觀,並在由activity管理的back stack中保存那些變化.(http://developer.android.com/guide/topics/fundamentals/fragments.html) html
好比, 一個新聞應用可以在屏幕左側使用一個fragment來展現一個文章的列表,而後在屏幕右側使用還有一個fragment來展現一篇文章--2個fragment並排顯示在相同的一個activity中,並且每一個fragment擁有它本身的一套生命週期回調方法,並且處理它們本身的用戶輸入事件。 所以, 代替使用一個activity來選擇一篇文章而還有一個activity來閱讀文章的方式,用戶可以在同一個activity中選擇一篇文章並且閱讀, 如圖所看到的:java
fragment在你的應用中應當是一個模塊化和可重用的組件.即,因爲fragment定義了它本身的佈局, 以及經過使用它本身的生命週期回調方法定義了它本身的行爲,你可以將fragment包括到多個activity中. 這點特別重要, 因爲這贊成你將你的用戶體驗適配到不一樣的屏幕尺寸.舉個樣例,你可能會僅當在屏幕尺寸足夠大時,在一個activity中包括多個fragment,並且,當不屬於這樣的狀況時,會啓動還有一個單獨的,使用不一樣fragment的activity.
繼續以前那個新聞的樣例 -- 當運行在一個特別大的屏幕時(好比平板電腦),應用可以在Activity A中嵌入2個fragment。然而,在一個正常尺寸的屏幕(好比手機)上,沒有足夠的空間同一時候供2個fragment用, 所以, Activity A會僅包括文章列表的fragment, 而當用戶選擇一篇文章時, 它會啓動ActivityB,它包括閱讀文章的fragment.所以, 應用可以同一時候支持上圖中的2種設計模式。android
一般, 應當至少實現例如如下的生命週期方法:設計模式
其生命週期圖例如如下:ide
大多數應用應當爲每一個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:函數
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個參數:工具
<?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類.
有3種方法來爲一個fragment提供一個標識:佈局
FragmentManager fragmentManager =getFragmentManager(); FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();
而後你可以使用 add() 方法加入一個fragment, 指定要加入的fragment和要插入的view.動畫
ExampleFragment fragment = newExampleFragment(); fragmentTransaction.add(R.id.fragment_container,fragment); fragmentTransaction.commit();
add()的第一個參數是fragment要放入的ViewGroup, 由resource ID指定,第二個參數是需要加入的fragment.一旦用FragmentTransaction作了改變,爲了使改變生效,必須調用commit().
要在activity中管理fragment,需要使用FragmentManager. 經過調用activity的getFragmentManager()取得它的實例.
可以經過FragmentManager作一些事情, 包括:
關於在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 = newExampleFragment(); FragmentTransaction transaction =getFragmentManager().beginTransaction(); // Replace whatever is in thefragment_container view with this fragment, // and add the transaction to the backstack 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的順序不重要, 除下面例外:
雖然Fragment被實現爲一個獨立於Activity的對象,並且可以在多個activity中使用,但一個給定的fragment實例是直接綁定到包括它的activity的. 特別的,fragment可以使用 getActivity() 訪問Activity實例, 並且easy地運行比方在activity layout中查找一個view的任務.
View listView =getActivity().findViewById(R.id.list);
相同地,activity可以經過從FragmentManager得到一個到Fragment的引用來調用fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().
ExampleFragment fragment =(ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
在一些狀況下, 你可能需要一個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中聲明:
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 implementOnArticleSelectedListener"); } } ... }
假設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 獲取文章.
你的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 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的推入它本身的生命週期過程.
(關於Example,興許)