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:
- 爲 android:id 屬性提供唯一 ID
- 爲 android:tag 屬性提供唯一字符串
- 如果未給以上兩個屬性提供值,系統會使用容器視圖的 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中添加一個Fragmentremove()
從Activity中移除一個Fragmentreplace()
使用另一個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 用法總結(二)