Fragment學(xué)習(xí)

Fragment簡(jiǎn)介

Fragment(翻譯為片段)可以作為Activity 布局的一部分摆尝,必須依托于Activity愕宋,但又擁有自己的生命周期。其生命周期同時(shí)受到宿主Activity生命周期的影響结榄。例如,當(dāng) Activity 暫停時(shí)囤捻,Activity 的所有片段也會(huì)暫停臼朗;當(dāng) Activity 被銷毀時(shí),所有片段也會(huì)被銷毀。

不過(guò)视哑,當(dāng) Activity 正在運(yùn)行(處于onResume生命周期狀態(tài))時(shí)绣否,您可以獨(dú)立操縱每個(gè)片段,如添加或移除片段挡毅。當(dāng)執(zhí)行此類片段事務(wù)時(shí)蒜撮,您也可將其添加到由 Activity 管理的返回棧 — Activity 中的每個(gè)返回棧條目都是一條已發(fā)生片段事務(wù)的記錄。

生命周期

image.png

其中跪呈,onCreate()段磨、onStart()onPause()onStop()方法和Activity 非常相似耗绿。所以Google官方稱Fragment有點(diǎn)像可以在不同 Activity 中重復(fù)使用的“子 Activity”苹支。

onAttach():在片段已與 Activity 關(guān)聯(lián)時(shí)進(jìn)行調(diào)用(Activity 傳遞到此方法內(nèi))。

onCreate():系統(tǒng)會(huì)在創(chuàng)建片段時(shí)調(diào)用此方法误阻。

onCreateView():系統(tǒng)會(huì)在片段首次繪制其界面布局時(shí)調(diào)用此方法债蜜。所以返回的 View 必須是片段布局的根視圖。為方便返回視圖究反,onCreateView() 提供(傳入)了一個(gè) LayoutInflater 對(duì)象寻定,直接使用即可。

onActivityCreated():當(dāng) Activity 的 onCreate() 方法已返回時(shí)進(jìn)行調(diào)用精耐。

onCreate()狼速、onStart()onPause()onStop()與Activity的生命周期方法一致黍氮。

//布局的資源 ID唐含,將作為擴(kuò)展布局父項(xiàng)的 ViewGroup,
//是否在擴(kuò)展期間將擴(kuò)展布局附加至 ViewGroup【這里傳TRUE則會(huì)多一個(gè)擴(kuò)展視圖】
return inflater.inflate(R.layout.example_fragment, container, false)

基本使用

  • 1.布局中使用(靜態(tài)使用)
    <fragment
        android:id="@+id/fragment"
        android:name=".DemoFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

注意別忘了id沫浆。

  • 2.代碼中使用(動(dòng)態(tài)添加)

首先xml文件中得有個(gè)存放該Fragment的ViewGroup捷枯。不知道用哪個(gè)viewGroup就寫androidx.fragment.app.FragmentContainerView

如要在您的 Activity 中執(zhí)行片段事務(wù)(如添加专执、移除或替換片段)淮捆,則必須使用 FragmentTransaction 中的 API。如下所示本股,您可以從 FragmentActivity 獲取一個(gè) FragmentTransaction 實(shí)例:

val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()

然后攀痊,您可以使用 add() 方法添加一個(gè)片段,指定要添加的片段以及將其插入哪個(gè)視圖拄显。例如:

val fragment = ExampleFragment()
fragmentTransaction.add(R.id.fragment_container, fragment)
fragmentTransaction.commit()

傳遞到 add() 的第一個(gè)參數(shù)是 ViewGroup的id苟径,即應(yīng)放置片段的位置,由資源 ID 指定躬审,第二個(gè)參數(shù)是要添加的片段棘街。

一旦您通過(guò) FragmentTransaction 做出了更改蟆盐,就必須調(diào)用 commit() 以使更改生效。


上面代碼中用到了fragmentManager對(duì)象遭殉,它是用來(lái)管理Fragment的類石挂。比如:

  • 通過(guò) findFragmentById()(針對(duì)在 Activity 布局中提供界面的片段)或 findFragmentByTag()(針對(duì)提供或不提供界面的片段)獲取 Activity 中存在的片段。
  • 通過(guò) popBackStack()(模擬用戶發(fā)出的返回命令)使片段從返回棧中彈出险污。
  • 通過(guò) addOnBackStackChangedListener() 注冊(cè)偵聽(tīng)返回棧變化的偵聽(tīng)器痹愚。

從manager中獲取到的管理相關(guān)類中有一個(gè)FragmentTransaction。transaction譯為交易蛔糯,這里指片段執(zhí)行添加(add)拯腮、移除(remove)、替換(replace)以及其他操作渤闷,從而響應(yīng)用戶交互疾瓮,所以官網(wǎng) 稱之為片段事務(wù)

然后飒箭,如要將事務(wù)應(yīng)用到 Activity狼电,您必須調(diào)用 commit()

在調(diào)用 commit() 之前弦蹂,可以調(diào)用 addToBackStack()肩碟,以將事務(wù)添加到片段事務(wù)返回棧。該返回棧由 Activity 管理凸椿,允許用戶通過(guò)按返回按鈕返回上一片段狀態(tài)削祈。

提示:對(duì)于每個(gè)片段事務(wù),您都可通過(guò)在提交前調(diào)用 setTransition() 來(lái)應(yīng)用過(guò)渡動(dòng)畫脑漫。

image.png

與 Activity 通信

Fragment中獲取Activity對(duì)象

片段可通過(guò) getActivity() 訪問(wèn) FragmentActivity 實(shí)例髓抑,并輕松執(zhí)行在 Activity 布局中查找視圖等任務(wù):

val listView: View? = activity?.findViewById(R.id.list)

Activity中獲取Fragment

主要是用到了上文說(shuō)的fragmentManager來(lái)獲取。

val fragment = supportFragmentManager.findFragmentById(R.id.example_fragment) as ExampleFragment

既然能取到Fragment對(duì)象优幸,便可在Fragment中定義一個(gè)接口吨拍,然后在Activity中實(shí)現(xiàn)相關(guān)方法,從而完成事件回調(diào)网杆。如下:

public class FragmentA : ListFragment() {

    var listener: OnArticleSelectedListener? = null
    ...
    override fun onAttach(context: Context) {
        super.onAttach(context)
        listener = context as? OnArticleSelectedListener
        if (listener == null) {
            throw ClassCastException("$context must implement OnArticleSelectedListener")
        }

    }
     // Container Activity must implement this interface
    interface OnArticleSelectedListener {
        fun onArticleSelected(articleUri: Uri)
    }
    ...
}

更多情況下羹饰,Google官方更推薦我們使用ViewModle來(lái)進(jìn)行在片段之間的共享數(shù)據(jù)。上面代碼只提供了事件處理和控件共享碳却。

向應(yīng)用欄(ActionBar/Toolbar)添加項(xiàng)目

您的片段可通過(guò)實(shí)現(xiàn) onCreateOptionsMenu() 向 Activity 的選項(xiàng)菜單(并因此向應(yīng)用欄)貢獻(xiàn)菜單項(xiàng)队秩。不過(guò),為使此方法能夠收到調(diào)用昼浦,您必須在 onCreate() 期間調(diào)用 setHasOptionsMenu()馍资,以指示片段想要向選項(xiàng)菜單添加菜單項(xiàng)。否則关噪,片段不會(huì)收到對(duì) onCreateOptionsMenu() 的調(diào)用鸟蟹。

選定菜單項(xiàng)時(shí)物舒,片段還會(huì)收到對(duì) onOptionsItemSelected() 的回調(diào)∠非拢【是通過(guò)Activity 的回調(diào)傳遞而來(lái),如果 Activity 對(duì) on-item-selected 回調(diào)的實(shí)現(xiàn)不處理選定的菜單項(xiàng)火诸,則系統(tǒng)會(huì)將事件傳遞至片段的回調(diào)锦针。】

您還可通過(guò)調(diào)用 registerForContextMenu()置蜀,在片段布局中注冊(cè)一個(gè)視圖來(lái)提供上下文菜單奈搜。當(dāng)用戶打開(kāi)上下文菜單時(shí),片段會(huì)收到對(duì) onCreateContextMenu() 的調(diào)用盯荤。當(dāng)用戶選擇某個(gè)菜單項(xiàng)時(shí)馋吗,片段會(huì)收到對(duì) onContextItemSelected() 的調(diào)用。

Fragment之間數(shù)據(jù)傳遞

Fragment 1.3.0-alpha04 開(kāi)始秋秤,每個(gè) FragmentManager 都會(huì)實(shí)現(xiàn) FragmentResultOwner宏粤。即數(shù)據(jù)存放在Fragment中,F(xiàn)ragment彼此之間無(wú)須彼此引用灼卢,只需監(jiān)聽(tīng)結(jié)果即可绍哎。

如需將數(shù)據(jù)從 Fragment B 傳回到 Fragment A,只需要在A中設(shè)置監(jiān)聽(tīng)器 setFragmentResultListener(),B中使用 setFragmentResult() API進(jìn)行操作即可鞋真。

FragmentA:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setResultListener("requestKey") { key, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result...
    }
}

FragmentB:

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

在父級(jí) Fragment 和子級(jí) Fragment 之間傳遞結(jié)果

如需將結(jié)果從子級(jí) Fragment 傳遞到父級(jí) Fragment崇堰,父級(jí) Fragment 在調(diào)用 setFragmentResultListener() 時(shí)應(yīng)使用 getChildFragmentManager()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // We set the listener on the child fragmentManager
    childFragmentManager.setResultListener("requestKey") { key, bundle ->
        val result = bundle.getString("bundleKey")
        // Do something with the result..
    }
}

最后涩咖,F(xiàn)ragment作為一個(gè)獨(dú)立的界面組件海诲,是可復(fù)用的。所以Google再次推薦使用ViewModle+LiveData的方式進(jìn)行通信檩互。

常見(jiàn)錯(cuò)誤:

Binary XML file line #?? Error inflating class fragment

  1. 先檢查是否給了id 和name;
  2. 如果是動(dòng)態(tài)加載特幔,注意別用<fragment> 標(biāo)簽,改用一個(gè)ViewGroup盾似,不知道用哪個(gè)就寫FragmentContainerView;
  3. 檢查導(dǎo)包有沒(méi)有導(dǎo)錯(cuò)敬辣,有v4下的,也有androidx下的零院,別弄混了溉跃。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市告抄,隨后出現(xiàn)的幾起案子撰茎,更是在濱河造成了極大的恐慌,老刑警劉巖打洼,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件龄糊,死亡現(xiàn)場(chǎng)離奇詭異逆粹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)炫惩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門僻弹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人他嚷,你說(shuō)我怎么就攤上這事蹋绽。” “怎么了筋蓖?”我有些...
    開(kāi)封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵卸耘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我粘咖,道長(zhǎng)蚣抗,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任瓮下,我火速辦了婚禮翰铡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘唱捣。我一直安慰自己两蟀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布震缭。 她就那樣靜靜地躺著赂毯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拣宰。 梳的紋絲不亂的頭發(fā)上党涕,一...
    開(kāi)封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音巡社,去河邊找鬼膛堤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛晌该,可吹牛的內(nèi)容都是我干的肥荔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼朝群,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼燕耿!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起姜胖,我...
    開(kāi)封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤誉帅,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蚜锨,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡档插,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亚再。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郭膛。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氛悬,靈堂內(nèi)的尸體忽然破棺而出饲鄙,到底是詐尸還是另有隱情,我是刑警寧澤圆雁,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站帆谍,受9級(jí)特大地震影響伪朽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汛蝙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一烈涮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窖剑,春花似錦坚洽、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至需了,卻和暖如春跳昼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肋乍。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工鹅颊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人墓造。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓堪伍,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親觅闽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子帝雇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355