Fragment建立動(dòng)態(tài)UI

編寫: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)用 mCallbackOnHeadlineSelectedListener 接口的實(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();
        }
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末著觉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惊暴,更是在濱河造成了極大的恐慌饼丘,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辽话,死亡現(xiàn)場離奇詭異肄鸽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屡穗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門贴捡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忽肛,“玉大人村砂,你說我怎么就攤上這事∫俟洌” “怎么了础废?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長罕模。 經(jīng)常有香客問我评腺,道長,這世上最難降的妖魔是什么淑掌? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任蒿讥,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芋绸。我一直安慰自己媒殉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布摔敛。 她就那樣靜靜地躺著廷蓉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪马昙。 梳的紋絲不亂的頭發(fā)上桃犬,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音行楞,去河邊找鬼攒暇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛子房,可吹牛的內(nèi)容都是我干的扯饶。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼池颈,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼尾序!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起躯砰,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤每币,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后琢歇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兰怠,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年李茫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了揭保。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡魄宏,死狀恐怖秸侣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宠互,我是刑警寧澤味榛,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站予跌,受9級(jí)特大地震影響搏色,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜券册,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一频轿、第九天 我趴在偏房一處隱蔽的房頂上張望垂涯。 院中可真熱鬧,春花似錦航邢、人聲如沸集币。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞠苟。三九已至,卻和暖如春秽之,著一層夾襖步出監(jiān)牢的瞬間当娱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國打工考榨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跨细,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓河质,卻偏偏與公主長得像冀惭,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掀鹅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容