Android: Fragment編程指南

本文來自於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裏實現選擇文章和閱讀文章。如下圖所示:

Image1

你應當設計每一個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

Image2

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 = getFragmentManager()
  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

然後調用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 = getActivity().findViewById(R.id.list);

同樣的,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.withAppendedId(ArticleColumns.CONTENT_URI, id);
          // Send the event and Uri to the host activity
          mListener.onArticleSelected(noteUri);
      }
      ...
  }

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。如下圖所示:

Image3

 本文檔完整的pdf版本,請參考“Android Fragment指南.pdf

(如需轉發,請標明出處)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章