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ù)的記錄。
生命周期
其中跪呈,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)畫脑漫。
與 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
- 先檢查是否給了id 和name;
- 如果是動(dòng)態(tài)加載特幔,注意別用<fragment> 標(biāo)簽,改用一個(gè)ViewGroup盾似,不知道用哪個(gè)就寫
FragmentContainerView
; - 檢查導(dǎo)包有沒(méi)有導(dǎo)錯(cuò)敬辣,有v4下的,也有androidx下的零院,別弄混了溉跃。