Fragment 用法總結(一)

Fragment 用法總結(一)

Fragment有點類似View,一種增強版的View,不僅可以show、hide,而且有大量的生命週期方法,處理事件,更好的和Activity交互,在顯示title和content的層級界面上有獨特的優勢,更好的兼容適應平板和手機。下面主要分三個部分講解Fragment的創建及基本用法、生命週期和高級用法。

本文主要參考官方文檔並加入自己整理的內容。

創建fragment

創建fragment必須要繼承android.app.Fragment,還有下面幾種除了Fragment基類的可擴展子類可以繼承。

  • DialogFragment
    浮動對話框。使用DialogFragment創建對話框可有效地替代使用 Activity 類中的對話框幫助程序方法,因爲可以將Fragment對話框納入由 Activity 管理的Fragment返回棧,從而使用戶能夠返回清除的Fragment。
  • ListFragment
    顯示由適配器(如 SimpleCursorAdapter)管理的一系列項目,類似於 ListActivity。它提供了幾種管理列表視圖的方法,如用於處理點擊事件的 onListItemClick() 回調。
  • PreferenceFragment
    以列表形式顯示 Preference 對象的層次結構,類似於 PreferenceActivity。這在爲應用創建“設置” Activity 時很有用處。

如果使用Android3.0以下的版本,需要引入support.v4的包。

添加到Activity

使用佈局

在 Activity 的佈局文件內聲明Fragment
可以將Fragment當作視圖來爲其指定佈局屬性。 例如:

<fragment
    android:id="@+id/fragment"
    android:name="com.xfdsj.fragment.FragmentA"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:layout="@layout/fragment_main"/>

<fragment> 中的 android:name 屬性指定要在佈局中實例化的 Fragment 類。

當系統創建此 Activity 佈局時,會實例化在佈局中指定的每個Fragment,併爲每個Fragment調用 onCreateView() 方法,以檢索每個Fragment的佈局。系統會直接插入Fragment返回的 View 來替代 <fragment> 元素。

每個Fragment都需要一個唯一的標識符,重啓 Activity 時,系統可以使用該標識符來恢復Fragment(也可以使用該標識符來捕獲Fragment以執行某些事務,如將其刪除)。 可以通過三種方式爲Fragment提供 ID:

  1. 爲 android:id 屬性提供唯一 ID
  2. 爲 android:tag 屬性提供唯一字符串
  3. 如果未給以上兩個屬性提供值,系統會使用容器視圖的 ID

動態添加

通過編程方式將Fragment添加到某個現有 ViewGroup
在 Activity 運行期間隨時可以將Fragment添加到 Activity 佈局中。只需指定將Fragment放入哪個 ViewGroup

要想在 Activity 中執行Fragment事務(如添加、刪除或替換Fragment),必須使用 FragmentTransaction 中的 API。可以從 Activity 獲取一個 FragmentTransaction 實例:

FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

然後可以使用 add() 方法添加一個Fragment,指定要添加的Fragment以及將其插入哪個視圖。例如:

FragmentA fragment = new FragmentA();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

傳遞到 add() 的第一個參數是 ViewGroup,即應該放置Fragment的位置,由資源 ID 指定,第二個參數是要添加的Fragment。

一旦通過 FragmentTransaction 做出了更改,就必須調用 commit() 以使更改生效。

管理Fragment

要想管理 Activity 中的Fragment,需要使用 FragmentManager。要想獲取它,從 Activity 調用 getFragmentManager()

FragmentManager 可以執行的操作包括:

  • 通過 findFragmentById()(對於在 Activity 佈局中提供 UI 的Fragment)或 findFragmentByTag()(對於提供或不提供 UI 的Fragment)獲取 Activity 中存在的Fragment
  • 通過 popBackStack()(模擬用戶發出的 Back 命令)將Fragment從返回棧中彈出
  • 通過 addOnBackStackChangedListener() 註冊一個偵聽返回棧變化的偵聽器

如上文所說的,也可以使用 FragmentManager 打開一個 FragmentTransaction,通過它來執行某些事務,如添加和刪除Fragment。

執行Fragment事物

在 Activity 中使用Fragment的一大優點是,可以根據用戶行爲對Fragment執行添加、刪除、替換以及其他操作。 commit給 Activity 的每組更改都稱爲事務,FragmentTransaction 中的 API 用來執行事務。我們也可以將每個事務保存到由 Activity 管理的返回棧內,從而讓用戶能夠回退Fragment更改(類似於回退 Activity)。

下面的代碼表示如何從 FragmentManager 獲取一個FragmentTransaction 實例:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

事務都是原子性的,同時執行的一組更改。事物的方法都有:

  • add() 向Activity中添加一個Fragment
  • remove() 從Activity中移除一個Fragment
  • replace() 使用另一個Fragment替換當前的
  • hide() 隱藏當前的Fragment,不會調用生命週期回調方法
  • show() 顯示之前隱藏的Fragment,不會調用生命週期回調方法
  • detach() 會將View從UI中移除,detach之後可以調用attach恢復視圖。
  • attach() 重建view視圖,附加到UI上並顯示。

這些方法爲給定事務設置我們想要執行的所有更改。然後,要想將事務應用到 Activity,必須調用 commit()。

在調用 commit() 之前,我們可能會想到調用 addToBackStack(),以便將事務添加到Fragment事務返回棧。 該返回棧由 Activity 管理,允許用戶通過按“返回” 按鈕返回上一Fragment狀態。

例如,以下示例說明了如何將一個Fragment替換成另一個Fragment,以及如何在返回棧中保留先前狀態:

    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.content_main, fragmentB).addToBackStack(null).commit();

在上例中,fragmentB 會替換目前在 R.id.content_main ID 所標識的佈局容器中的任何Fragment(如有)。通過調用 addToBackStack() 可將替換事務保存到返回棧,以便用戶能夠通過按“返回” 按鈕撤消事務並回退到上一Fragment。上面用到replace方法,下一篇會講到這些API對Fragment的生命週期的影響。

如果向事務添加了多個更改(如又一個 add() 或 remove()),並且調用了 addToBackStack(),則在調用 commit() 前應用的所有更改都將作爲單一事務添加到返回棧,並且“返回” 按鈕會將它們一併撤消。

向 FragmentTransaction 添加更改的順序無關緊要,不過:

  • 必須在最後調用 commit()
  • 如果要向同一容器添加多個Fragment,則添加Fragment的順序將決定它們在視圖層次結構中的出現順序

如果在執行刪除Fragment的事務時沒有調用 addToBackStack(),則事務提交時該Fragment會被銷燬,用戶將無法回退到該Fragment。 如果在刪除Fragment時調用了 addToBackStack(),則系統會停止該Fragment,並在用戶回退時將其恢復。addToBackStack() 方法可接受可選的字符串參數,來爲事務指定獨一無二的名稱。除非你打算使用 FragmentManager.BackStackEntry API 執行高級 Fragment 操作,否則無需爲addToBackStack()設置非空字符串參數。

提示:對於每個Fragment事務,都可以通過在提交前調用 setTransition() 來應用過渡動畫。

調用 commit() 不會立即執行事務,而是在 Activity 的 UI 線程(“主”線程)可以執行該操作時再安排其在線程上運行。不過,如有必要,也可以從 UI 線程調用 executePendingTransactions() 以立即執行 commit() 提交的事務。通常不必這樣做,除非其他線程中的作業依賴該事務。

注意:只能在 Activity保存Fragment狀態(用戶離開 Activity)之前使用 commit() 提交事務。如果試圖在該時間點後提交,則會引發異常。 這是因爲如需恢復 Activity,則提交後的狀態可能會丟失。 對於丟失提交無關緊要的情況,請使用 commitAllowingStateLoss()

與 Activity 通信

儘管 Fragment 是作爲獨立於 Activity 的對象實現,並且可在多個 Activity 內使用,但Fragment的給定實例會直接綁定到包含它的 Activity。

具體地說,Fragment可以通過 getActivity() 訪問 Activity 實例,並輕鬆地執行在 Activity 佈局中查找視圖等任務。

View listView = getActivity().findViewById(R.id.list);

同樣地,Activity 也可以使用 findFragmentById() 或 findFragmentByTag(),通過從 FragmentManager 獲取對 Fragment 的引用來調用Fragment中的方法。例如:

FragmentA fragment = (FragmentA) getFragmentManager().findFragmentById(R.id.content_main);

創建對 Activity 的事件回調

在某些情況下,可能需要通過Fragment與 Activity 共享事件。執行此操作的一個好方法是,在Fragment內定義一個回調接口,並要求宿主 Activity 實現它。 當 Activity 通過該接口收到回調時,可以根據需要與佈局中的其他Fragment共享這些信息。

例如,一個 Activity 有兩個個Fragment 分別是FragmentA、FragmentB,FragmentA隨Activity創建時創建,點擊A則切換到FragmentB,如圖:
這裏寫圖片描述
在Fragment A中聲明ActionA接口:

public class FragmentA extends Fragment {
  ActionA actionA;

  // Container Activity must implement this interface
  public interface ActionA {
    void actionA();
  }
  ...
}

然後,該Fragment的宿主 Activity 會實現 ActionA 接口並實現 actionA(),並在方法內實現替換FragmentA爲FragmentB:

public class MainActivity extends Activity implements FragmentA.ActionA {
  ...
  @Override public void actionA() {
    if (fragmentB == null) {
      fragmentB = new FragmentB();
    }
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.content_main, fragmentB).addToBackStack(null).commit();
  }
  ...
}

將來自FragmentA 的事件通知給保宿主 Activity,FragmentA 的 onAttach() 回調方法(系統在向 Activity 添加Fragment時調用的方法)會通過轉換傳遞到 onAttach() 中的 Activity 來實例化 ActionA 接口的實例:

public class FragmentA extends Fragment {
  ...
  @Override public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
      if (activity instanceof ActionA) {
        actionA = (ActionA) activity;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  ...
}

實現時,actionA 成員會保留對 Activity 的 ActionA 實現的引用,以便FragmentA 點擊事件發生時候可以通知Activity 替換當前Fragment。例如,點擊FragmentA中的imageView,就會觸發事件:

public class FragmentA extends Fragment {
  ...
  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View view = inflater.inflate(R.layout.f_a, container, false);
    ImageView imageView = (ImageView) view.findViewById(R.id.btn);
    imageView.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View view) {
        if (actionA != null) {
          actionA.actionA();
        }
      }
    });
    return view;
  }
  ...
}

其它交互方式

  • Fragment操作Fragment
    一般不建議這樣做,除非有特別的需求,Activity擔任的是Fragment間類似總線一樣的角色,應當由它決定Fragment如何操作,在結構上如果由Fragment管理會變得混亂。
  • 使用Activity的newIntent()
    Fragment通過啓動宿主Activity(設置啓動模式爲singleTop),通過傳入參數的方式判斷顯示哪個Fragment。

上面的Demo

上面講了Fragment的一些常用方法,下一篇Fragment 用法總結(二)

發佈了42 篇原創文章 · 獲贊 216 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章