本文來自於www.lanttor.org
Fragment代表了Activity裏的一個行爲,或者Activity UI的一部分。你可以在一個activity裏構造多個Fragment,也可以在多個activities裏複用一個Fragment。你可以認爲Fragment是activity裏的一個模塊片段。Fragment有自己的lifecycle,接收自己的input事件。你可以在activity運行的時候添加或者刪除一個Fragment。
一個fragment必須嵌在一個activity裏,它的lifecycle直接受到主activity的lifecycle的影響。例如,當一個activity 處於paused狀態時,所有的fragment也處於paused狀態;當一個activity處於destroyed狀態時,所有的fragment也是destroyed狀態。然而,當一個activity處於運行狀態時(it is in the resumed lifecycle state),你可以獨立的操作每個fragment,例如添加或刪除它。當你執行一個fragment事務處理時,你可以將該操作加入到activity管理的back stack裏---每一個back stack項是記錄發生的一個fragment事務。back stack允許你通過回退按鈕來回退一個fragment事務處理。
當你在你的activity layout裏添加一個fragment時,它駐紮於activity 的一個viewgroup裏。
Fragment定義自己的視圖佈局。你可以在你的activity layout裏使用<fragment>元素插入一個fragment,也可以在代碼裏添加一個fragment到已存在的ViewGroup裏。然而,fragment的視圖佈局不是必須的,你也可以使用沒有UI的fragement,作爲你的activity的一個不可見的worker。
1. 設計指南
Android的fragment機制,是在Android 3.0 (API level 11)上引進的,主要是支持大尺寸屏幕上的動態和靈活的UI設計,例如平板電腦。由於平板的尺寸遠遠大於手機設備,它有足夠的空間來進行UI界面的組合和交互。Fragment機制可以讓你避免複雜的視圖結構的管理,來實現UI的動態交互。將activity裏的佈局,分解成多個fragment,你能夠在activity運行時動態改變activity的外觀,在back stack裏維持這些改變。
例如,一個應用可以使用一個fragment在左側顯示文章的標題,使用另一個fragment在右側顯示文章的內容---兩個fragment都在同一個activity裏,每個fragment有自己獨立的一套lifecycle,可以處理自己的用戶輸入事件。這樣,可以規避用一個activity來實現選擇文章,另一個activity來實現閱讀文章的舊方法;利用fragment,可以在一個activity裏實現選擇文章和閱讀文章。如下圖所示:
你應當設計每一個fragment,作爲activity裏可複用的組件模塊。因爲每個fragment定義自己的layout,自己的行爲,自己的lifecycle callback,你可以在多個activity裏使用一個fragment。一個模塊化的fragment,允許在不同尺寸的設備上改變fragment的排列。當你打算涉及你的應用既支持平板又支持手機時,你可以在不同的佈局配置上覆用你的fragment,根據相應的顯示空間優化用戶的體驗。例如,在手機上,fragment可能只能同時顯示一屏UI界面。
2. 創建一個Fragment
創建一個fragment,你必須實現一個類繼承fragment類。Fragment類包含了與activity類相似的回調函數,例如onCreate(),onStart(),onPause(),onStop()。實際上,你可以簡單的將activity裏實現的回調函數拷貝到你的fragment類裏,進行修改。
通常,你至少需要考慮實現下面的回調函數:
onCreate()
當創建fragment時系統會調用此函數。你應當在這裏初始化fragment必須的組件。
onCreateView()
當fragment繪畫UI界面時系統調用此函數。這個方法必須返回一個View,作爲fragment的根佈局。
onPause()
當用戶離開此fragment界面時,系統調用此函數。你應當在這裏保存你需要維持的數據。
基於fragment類,系統已實現了下面的一些擴展子類:
- DialogFragment
- ListFragment
- PreferenceFragment
3. 添加一個fragment UI界面
要爲一個fragment提供佈局,必須實現onCreateView()回調函數。該函數返回的View就是你的fragment的根佈局。
說明:如果你的fragment是由ListFragment擴展而來,默認的onCreateView()實現會返回一個ListView,所以你的代碼裏不必實現它。
示例如下:
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); } } |
4. 在activity裏添加一個fragment
有兩種方法實現:
- 在activity的layout文件裏聲明fragment元素。示例如下:
<?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屬性定義了實例化此佈局的Fragment類。當系統創建這個layout,它會初始化layout定義的每個fragment,調用onCreatView()來獲取每個fragment的view。
- 在代碼裏,添加一個fragment到一個已經存在的ViewGroup裏。
在activity運行時,你隨時可以在你的activity佈局裏添加fragment。你僅僅需要爲放置的fragment指定一個ViewGroup。爲了在你的activity裏進行fragment事務處理,你必須使用FragmentTransaction的API函數。用如下代碼獲得FragmentTransaction:
FragmentManager fragmentManager = |
然後調用add()來添加一個fragment:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit(); |
一旦調用FragmentTransaction的事務處理函數後,必須在最後調用commit()函數來使其生效。
5. Fragment與Activity通信
fragment可以通過getActivity()裏訪問Activity的實例。例如查找Activity裏的一個view:
View listView = |
同樣的,activity可以通過FragmentManager的findFragmentbyId()或者findFragmentbyTag()來獲取到一個需要訪問的fragment:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); |
6. 創建事件回調函數
在一些case裏,你也許需要fragment和activity之間共享事件。一個很好的做法是,在fragment裏定義回調函數接口,在主activity裏去實現它。
例如,當一個應用需要在activity裏實現兩個fragment。fragment A用於顯示文章標題,fragment B用於顯示文章內容。當用戶選中一個標題時,fragment A必須告之Activity,讓其通知Fragment B來顯示文章內容。這個case裏,fragment A定義瞭如下的接口:
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... } |
主activity會實現這個接口,覆寫onArticleSelected()來通知Fragment B,來自於Fragment A的消息。Fragment A會在onAttach()裏獲取到這個接口的實現:
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"); } } ... } |
當用戶點擊一個litst item,系統會調用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.
|
7. 處理fragment lifecycle
與activity類似,fragment存在如下三種狀態:
- Resumed
fragment在運行的activity裏是可見的。
- Paused
其他的activity在切面,部分遮住了當前的主activity,或者是背景半透明的全部遮住了當前的activity。導致當前的主activty裏的fragment處於Paused狀態。
- Stopped
fragment不可見。要麼是主activity已經停止,要麼是fragment被移除,但是被添加到了back stack裏。
Activity停止時,默認情況下會被添加到back stack;Fragment停止時,必須顯示的調用addToBackStack(),才能夠被添加到back stack。
主activity的life cycle會直接影響到fragment的lifecycle。如下圖所示:
本文檔完整的pdf版本,請參考“Android Fragment指南.pdf”
(如需轉發,請標明出處)