編寫:fastcome1985 - 原文:https://developer.android.com/training/basics/fragments/creating.html
創(chuàng)建Fragment
可以把 Fragment 想象成 Activity 的模塊,它擁有自己的生命周期芳杏、接收輸入事件捏卓,可以在 Acvitity 運(yùn)行過程中添加或者移除(有點(diǎn)像“子 Activity”,可以在不同的 Activity 里重復(fù)使用)剑鞍。
創(chuàng)建Fragment
繼承并創(chuàng)建 Fragment,然后在關(guān)鍵的生命周期方法中插入代碼(就和在處理 Activity 時(shí)一樣)。
其中一個(gè)區(qū)別是:創(chuàng)建 Fragment 時(shí)长捧,必須重寫 onCreateView() 回調(diào)方法來定義布局晦闰。事實(shí)上氛濒,這是唯一一個(gè)為使 Fragment 運(yùn)行起來需要重寫的回調(diào)方法产场。比如,下面是一個(gè)自定義布局的示例 Fragment:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;
public class ArticleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 拉伸該 Fragment 的布局
return inflater.inflate(R.layout.article_view, container, false);
}
}
和 Activity 一樣舞竿,當(dāng) Fragment 從 Activity 添加或者移除京景、或 Activity 生命周期發(fā)生變化時(shí),F(xiàn)ragment 通過生命周期回調(diào)函數(shù)管理其狀態(tài)骗奖。例如确徙,當(dāng) Activity 的 onPause() 被調(diào)用時(shí),它內(nèi)部所有 Fragment 的 onPause() 方法也會(huì)被觸發(fā)执桌。
更多關(guān)于 Fragment 的聲明周期和回調(diào)方法鄙皇,詳見 Fragments 開發(fā)指南.
用 XML 將 Fragment 添加到 Activity
Fragments 是可重用的、模塊化的 UI 組件仰挣。每個(gè) Fragment 實(shí)例都必須與一個(gè) FragmentActivity 關(guān)聯(lián)伴逸。我們可以在 Activity 的 XML 布局文件中逐個(gè)定義 Fragment 來實(shí)現(xiàn)這種關(guān)聯(lián)。
注: FragmentActivity 是 Support Library 提供的一種特殊 Activity膘壶,用于處理 API 11 版本以下的 Fragment错蝴。如果我們 APP 中的最低版本大于等于 11,則可以使用普通的 Activity颓芭。
以下是一個(gè) XML 布局的例子:當(dāng)屏幕被認(rèn)為是 "large"(用目錄名稱中的 large
字符來區(qū)分)時(shí)顷锰,它在布局中增加了兩個(gè) Fragment。
res/layout-large/news_articles.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="@+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="@+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
提示: 更多關(guān)于不同屏幕尺寸創(chuàng)建不同布局的信息亡问,請(qǐng)閱讀 兼容不同屏幕尺寸官紫。
然后將這個(gè)布局文件用到 Activity 中。
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);
}
}
如果使用 v7 appcompat 庫州藕,Activity 應(yīng)該改為繼承自 AppCompatActivity束世,AppCompatActivity 是 FragmentActivity 的子類(更多關(guān)于這方面的內(nèi)容,請(qǐng)閱讀 添加 App Bar)床玻。
注: 當(dāng)通過 XML 布局文件的方式將 Fragment 添加進(jìn) Activity 時(shí)良狈,F(xiàn)ragment 是不能被動(dòng)態(tài)移除的。如果想要在用戶交互的時(shí)候把 Fragment 切入與切出笨枯,必須在 Activity 啟動(dòng)后薪丁,再將 Fragment 添加進(jìn) Activity。
使用Fragment創(chuàng)建動(dòng)態(tài)UI
你可以在 Activity 運(yùn)行時(shí)向其添加 Fragment馅精,而不用使用 <fragment>
元素在布局文件中為 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 的布局隐孽。
要用一個(gè) Fragment 替換另一個(gè) Fragment癌椿,Activity 的布局中需要包含一個(gè)作為 Fragment 容器的空 FrameLayout。
請(qǐng)注意菱阵,該文件名與XML中布局文件的名稱相同踢俄,但布局目錄沒有 large
這一限定符。因此晴及,此布局會(huì)在設(shè)備屏幕小于“l(fā)arge”的情況下使用都办,原因是尺寸較小的屏幕不適合同時(shí)顯示兩個(gè) Fragment。
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) {
// 不過震蒋,如果我們要從先前的狀態(tài)還原茸塞,則無需執(zhí)行任何操作而應(yīng)返回,否則
// 就會(huì)得到重疊的 Fragment查剖。
if (savedInstanceState != null) {
return;
}
// 創(chuàng)建一個(gè)要放入 Activity 布局中的新 Fragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// 如果此 Activity 是通過 Intent 發(fā)出的特殊指令來啟動(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 布局中通過 <fragment>
元素進(jìn)行定義笋庄,因此該 Activity 可以移除和替換這個(gè) Fragment效扫。
用一個(gè) Fragment 替換另一個(gè) Fragment
替換 Fragment 的步驟與添加 Fragment 的步驟相似,但需要調(diào)用 replace() 方法直砂,而非 add()菌仁。
請(qǐng)注意,當(dāng)你執(zhí)行替換或移除 Fragment 等 Fragment 事務(wù)時(shí)静暂,最好能讓用戶向后導(dǎo)航和“撤消”所做更改济丘。要通過 Fragment 事務(wù)允許用戶向后導(dǎo)航,你必須調(diào)用 addToBackStack(),然后再執(zhí)行 FragmentTransaction摹迷。
注: 當(dāng)你移除或替換 Fragment 并向返回堆棧添加事務(wù)時(shí)疟赊,已移除的 Fragment 會(huì)停止(而不是銷毀)。如果用戶向后導(dǎo)航峡碉,還原該 Fragment近哟,它會(huì)重新啟動(dòng)。如果你沒有向返回堆棧添加事務(wù)异赫,那么該 Fragment 在移除或替換時(shí)就會(huì)被銷毀椅挣。
替換 Fragment 的示例:
// 創(chuàng)建 Fragment 并為其添加一個(gè)參數(shù),用來指定應(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ù)添加到返回堆棧鼠证,以便用戶可以向后導(dǎo)航
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 執(zhí)行事務(wù)
transaction.commit();
addToBackStack() 方法可接受可選的字符串參數(shù),來為事務(wù)指定獨(dú)一無二的名稱靠抑。除非你打算使用 FragmentManager.BackStackEntry API 執(zhí)行高級(jí) Fragment 操作量九,否則無需使用此名稱。
Fragment之間的交互
定義接口
為了讓 Fragment 與包含它的 Activity 進(jìn)行交互颂碧,可以在 Fragment 類中定義一個(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)用。)
以下是 Fragment 與 Activity 交互的例子:
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 可以通過調(diào)用 mCallback
(OnHeadlineSelectedListener
接口的實(shí)例)的 onArticleSelected()
方法(也可以是其它方法)與 Activity 進(jìn)行消息傳遞。
例如煞聪,當(dāng)用戶點(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);
}
實(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) {
// 用戶從 HeadlinesFragment 選擇了一篇文章的標(biāo)題
// 在這里做點(diǎn)什么,以顯示該文章
}
}
向 Fragment 傳遞消息
宿主 Activity 通過 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) {
// 用戶從 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();
// 無論 fragment_container 視圖里是什么热康,用該 Fragment 替換它沛申。并將
// 該事務(wù)添加至回棧,以便用戶可以往回導(dǎo)航(譯注:回棧姐军,即 Back Stack铁材。
// 在有多個(gè) Activity 的 APP 中,將這些 Activity 按創(chuàng)建次序組織起來的
// 棧奕锌,稱為回棧)
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// 執(zhí)行事務(wù)
transaction.commit();
}
}
}