本文整理自:Google官方文檔中文學(xué)習(xí)手冊(cè),筆者省略了對(duì)自己幫助不大的章節(jié),拜讀原文請(qǐng)點(diǎn)鏈接。
一钠惩、動(dòng)態(tài)創(chuàng)建靈活的 Fragment
1.在運(yùn)行時(shí)向 Activity 添加 Fragment
你可以在 Activity 運(yùn)行時(shí)向其添加 Fragment,而不用使用 <fragment>標(biāo)簽在布局文件中為 Activity 定義 Fragment族阅。如果你打算在 Activity 運(yùn)行周期內(nèi)更改 Fragment篓跛,就必須這樣做。
要執(zhí)行添加或移除 Fragment 等事務(wù)坦刀,你必須使用 FragmentManager 創(chuàng)建一個(gè) FragmentTransaction愧沟,后者可提供用于執(zhí)行添加、移除鲤遥、替換以及其他 Fragment 事務(wù)的 API沐寺。
如果 Activity 中的 Fragment 可以移除和替換,你應(yīng)在調(diào)用 Activity 的 onCreate() 方法期間為 Activity 添加初始 Fragment(s)盖奈。
在處理 Fragment(特別是在運(yùn)行時(shí)添加的 Fragment)時(shí)混坞,請(qǐng)謹(jǐn)記以下重要規(guī)則:必須在布局中為 Fragment 提供 View 容器,以便保存 Fragment 的布局钢坦。
靜態(tài)布局一次只會(huì)顯示一個(gè) Fragment究孕。要用一個(gè) Fragment 替換另一個(gè) Fragment,Activity 的布局中需要包含一個(gè)作為 Fragment 容器的空 FrameLayout爹凹。
res/layout/news_articles.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在 Activity 中厨诸,用 Support Library API 調(diào)用 getSupportFragmentManager() 以獲取 FragmentManager,然后調(diào)用 beginTransaction() 創(chuàng)建 FragmentTransaction禾酱,然后調(diào)用 add() 添加 Fragment微酬。
你可以使用同一個(gè) FragmentTransaction 對(duì) Activity 執(zhí)行多 Fragment 事務(wù)。當(dāng)你準(zhǔn)備好進(jìn)行更改時(shí)颤陶,必須調(diào)用 commit()颗管。
例如,下面介紹了如何為上述布局添加 Fragment:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// 確認(rèn) Activity 使用的布局版本包含 fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// 不過(guò)指郁,如果我們要從先前的狀態(tài)還原忙上,則無(wú)需執(zhí)行任何操作而應(yīng)返回,否則
// 就會(huì)得到重疊的 Fragment闲坎。
if (savedInstanceState != null) {
return;
}
// 創(chuàng)建一個(gè)要放入 Activity 布局中的新 Fragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// 如果此 Activity 是通過(guò) Intent 發(fā)出的特殊指令來(lái)啟動(dòng)的疫粥,
// 請(qǐng)將該 Intent 的 extras 以參數(shù)形式傳遞給該 Fragment
firstFragment.setArguments(getIntent().getExtras());
// 將該 Fragment 添加到“fragment_container” FrameLayout 中
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}
由于該 Fragment 已在運(yùn)行時(shí)添加到 FrameLayout 容器中,而不是在 Activity 布局中通過(guò) <fragment>
元素進(jìn)行定義,因此該 Activity 可以移除和替換這個(gè) Fragment。
2.用一個(gè) Fragment 替換另一個(gè) Fragment
替換 Fragment 的步驟與添加 Fragment 的步驟相似达布,但需要調(diào)用 replace() 方法瘩燥,而非 add()溢豆。
請(qǐng)注意,當(dāng)你執(zhí)行替換或移除 Fragment 等 Fragment 事務(wù)時(shí)虑粥,最好能讓用戶(hù)向后導(dǎo)航和“撤消”所做更改很洋。要通過(guò) Fragment 事務(wù)允許用戶(hù)向后導(dǎo)航底哗,你必須調(diào)用 addToBackStack()岁诉,然后再執(zhí)行 FragmentTransaction。
注: 當(dāng)你移除或替換 Fragment 并向返回堆棧添加事務(wù)時(shí)跋选,已移除的 Fragment 會(huì)停止(而不是銷(xiāo)毀)涕癣。如果用戶(hù)向后導(dǎo)航,還原該 Fragment前标,它會(huì)重新啟動(dòng)坠韩。如果你沒(méi)有向返回堆棧添加事務(wù),那么該 Fragment 在移除或替換時(shí)就會(huì)被銷(xiāo)毀炼列。
替換 Fragment 的示例:
// 創(chuàng)建 Fragment 并為其添加一個(gè)參數(shù)只搁,用來(lái)指定應(yīng)顯示的文章
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 將 fragment_container View 中的內(nèi)容替換為此 Fragment,
// 然后將該事務(wù)添加到返回堆棧俭尖,以便用戶(hù)可以向后導(dǎo)航
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 執(zhí)行事務(wù)
transaction.commit();
addToBackStack() 方法可接受可選的字符串參數(shù)氢惋,來(lái)為事務(wù)指定獨(dú)一無(wú)二的名稱(chēng)。除非你打算使用 FragmentManager.BackStackEntry API 執(zhí)行高級(jí) Fragment 操作目溉,否則無(wú)需使用此名稱(chēng)明肮。
二、與其他 Fragment 交互
通常 Fragment 之間可能會(huì)需要交互缭付,比如基于用戶(hù)事件的內(nèi)容變更。所有 Fragment 之間的交互應(yīng)通過(guò)與之關(guān)聯(lián)的 Activity 來(lái)完成循未。兩個(gè) Fragment 之間不應(yīng)直接交互陷猫。
1.定義接口
為了讓 Fragment 與包含它的 Activity 進(jìn)行交互,可以在 Fragment 類(lèi)中定義一個(gè)接口的妖,并在 Activity 中實(shí)現(xiàn)绣檬。該 Fragment 在它的 onAttach() 方法生命周期中獲取該接口的實(shí)現(xiàn),然后調(diào)用接口的方法嫂粟,以便與 Activity 進(jìn)行交互娇未。(若該 Fragment 中實(shí)現(xiàn)了 onAttach() 方法,則會(huì)被自動(dòng)調(diào)用星虹。)
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// 容器 Activity 必須實(shí)現(xiàn)該接口
// (譯注:“容器 Activity”意即“包含該 Fragment 的 Activity”)
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// 確認(rèn)容器 Activity 已實(shí)現(xiàn)該回調(diào)接口零抬。否則,拋出異常
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
現(xiàn)在 Fragment 可以通過(guò)調(diào)用 mCallback(OnHeadlineSelectedListener 接口的實(shí)例)的 onArticleSelected() 方法(也可以是其它方法)與 Activity 進(jìn)行消息傳遞宽涌。
例如平夜,當(dāng)用戶(hù)點(diǎn)擊列表?xiàng)l目時(shí),F(xiàn)ragment 中的下面的方法將被調(diào)用卸亮。Fragment 用回調(diào)接口將事件傳遞給父 Activity忽妒。
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// 向宿主 Activity 傳送事件
mCallback.onArticleSelected(position);
}
2.實(shí)現(xiàn)接口
為了接收回調(diào)事件,宿主 Activity 必須實(shí)現(xiàn)在 Fragment 中定義的接口。
例如段直,下面的 Activity 實(shí)現(xiàn)了上面例子中的接口吃溅。
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// 用戶(hù)從 HeadlinesFragment 選擇了一篇文章的標(biāo)題
// 在這里做點(diǎn)什么,以顯示該文章
}
}
3.向 Fragment 傳遞消息
宿主 Activity 通過(guò) findFragmentById() 獲取 Fragment 的實(shí)例鸯檬,然后直接調(diào)用 Fragment 的 public 方法向 Fragment 傳遞消息罕偎。
例如,假設(shè)上面所示的 Activity 可能包含另一個(gè) Fragment京闰,該 Fragment 用于展示從上面的回調(diào)方法中返回的指定的數(shù)據(jù)颜及。在這種情況下,Activity 可以把從回調(diào)方法中接收到的信息傳遞到這個(gè)展示數(shù)據(jù)的 Fragment蹂楣。
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// 用戶(hù)從 HeadlinesFragment 選擇了一篇文章的標(biāo)題
// 在這里做點(diǎn)什么俏站,以顯示該文章
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// 若 articleFrag 有效,則表示我們正在處理兩格布局(two-pane layout)……
// 調(diào)用 ArticleFragment 的方法痊土,以更新其內(nèi)容
articleFrag.updateArticleView(position);
} else {
// 否則肄扎,我們正在處理單格布局(one-pane layout)。此時(shí)需要 swap frags...
// 創(chuàng)建 Fragment赁酝,向其傳遞包含被選文章的參數(shù)
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 無(wú)論 fragment_container 視圖里是什么犯祠,用該 Fragment 替換它。并將
// 該事務(wù)添加至回棧酌呆,以便用戶(hù)可以往回導(dǎo)航(譯注:回棧衡载,即 Back Stack。
// 在有多個(gè) Activity 的 APP 中隙袁,將這些 Activity 按創(chuàng)建次序組織起來(lái)的
// 棧痰娱,稱(chēng)為回棧)
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 執(zhí)行事務(wù)
transaction.commit();
}
}
}