1. 簡要
Fragment庫提供兩個通訊的選項,共享的View Model和Fragment Result API
如需共享持久性數據,應使用View Model
放置Bundle中一次性結果,應使用Fragment Result API
2. 使用View Model共享數據
以下為Android官網注意事項:
注意:ViewModel
会一直在内存中,直到其范围限定到的 ViewModelStoreOwner
永久消失。在一个 Activity 架构中,如果 ViewModel
的范围限定为 Activity,那么它本质上是单例。首次实例化 ViewModel
之后,使用 Activity 范围检索 ViewModel
的后续调用始终返回相同的现有 ViewModel
以及现有数据,直到 Activity 的生命周期永久结束。
注意:務必將適當的範圍與 ViewModelProvider 一起使用。在前面的示例中,MainActivity 用作 MainActivity 和 ListFragment 中的範圍,因此為它們提供了相同的 ViewModel。如果 ListFragment 將自身用作範圍,會為其提供與 MainActivity 不同的 ViewModel。
在多個Fragment之間或與Activity之間共享數據時,View Model是理想的選擇
第一種:在Activity監聽觀察變化
//新增一個Item class Item( var id: Int = 0, var name: String = "", var data: Int = 0) { } //建立一個ViewModel class ItemViewModel: ViewModel() { private val mutableSelectItem = MutableLiveData() val selectedItem get() = mutableSelectItem fun selectItem(item: Item) { mutableSelectItem.value = item } } //在Activity中新增 private val viewModel: ItemViewModel by viewModels() val fragment = TestFragment() //在onCreate中 supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment ) } viewModel.selectedItem.observe(this, { item -> println("item id = ${item.id}, " + "name = ${item.name}," + "data = ${item.data}" ) }) //在TestFragment中新增,此處按下按鈕後 //Activity就會打印出 //item id = 0, name = test, data = 2021 private val viewModel: ItemViewModel by activityViewModels() fun onItemClicked(item: Item) { viewModel.selectItem(item) } binding.itemImage.setOnClickListener { onItemClicked(Item().also { it.id = 0 it.name = "test" it.data = 2021 }) }
第二種:在兩個Fragment中監聽觀察變化
兩個Fragment都在Activity中
當資料變動可以刷新另一個Fragment
在FragmentA按下按鈕,由FragmentB接收並打印
viewModel item id = 0, name = test,data = 2021
在FragmentB按下按鈕,由FragmentA接收並打印
viewModel flag true
//新增一個ViewModel class ItemViewModel2: ViewModel() { private val mutableSelectItem = MutableLiveData() val selectedItem get() = mutableSelectItem val updateFlag = MutableLiveData() fun selectItem(item: Item) { mutableSelectItem.value = item } fun updateFlag(flag: Boolean) { updateFlag.value = flag } } //在Activity新增 val fragment = TestFragment() val fragment2 = TestFragment2() //onCreate中新增 supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment ) } supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view2, fragment2 ) } //在TestFragment新增 private val viewModel: ItemViewModel2 by activityViewModels() fun onItemClicked(item: Item) { viewModel.selectItem(item) } //在onViewCreated內新增 viewModel.updateFlag.observe(viewLifecycleOwner, { flag -> println("viewModel flag $flag") }) binding.itemImage.setOnClickListener { onItemClicked(Item().also { it.id = 0 it.name = "test" it.data = 2021 }) } //在TestFragment2新增 private val viewModel: ItemViewModel2 by activityViewModels() private var flag = false fun onItemClicked(flag: Boolean) { viewModel.updateFlag(flag) } //在onViewCreated中增新 viewModel.selectedItem.observe(viewLifecycleOwner, { item -> println("viewModel item id = ${item.id}, " + "name = ${item.name}," + "data = ${item.data}" ) }) binding.itemImage.setOnClickListener { flag = !flag onItemClicked(flag) }
第三種:父與子之間共享資料
一個Fragment在Activity中(父),另一個在Fragment中(子)
當資料變動可以刷新另一個Fragment
在FragmentA按下按鈕,由FragmentB接收並打印
viewModel item id = 0, name = test,data = 2021
在FragmentB按下按鈕,由FragmentA接收並打印
viewModel flag true
//新增一個ViewModel class ItemViewModel2: ViewModel() { private val mutableSelectItem = MutableLiveData() val selectedItem get() = mutableSelectItem val updateFlag = MutableLiveData() fun selectItem(item: Item) { mutableSelectItem.value = item } fun updateFlag(flag: Boolean) { updateFlag.value = flag } } //在Activity中新增 val fragment = TestFragment() //onCreate中新增 supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment ) } //在TestFragment中新增 private val viewModel: ItemViewModel2 by viewModels() val fragment2 = TestFragment2() fun onItemClicked(item: Item) { viewModel.selectItem(item) } //在onViewCreated中增新 childFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment2 ) } viewModel.updateFlag.observe(viewLifecycleOwner, { flag -> println("viewModel flag $flag") }) binding.itemImage.setOnClickListener { onItemClicked(Item().also { it.id = 0 it.name = "test" it.data = 2021 }) } //在TestFragment2中新增 private val viewModel: ItemViewModel2 by viewModels({requireParentFragment()}) private var flag = false fun onItemClicked(flag: Boolean) { viewModel.updateFlag(flag) } //在onViewCreated中增新 viewModel.selectedItem.observe(viewLifecycleOwner, { item -> println("viewModel item id = ${item.id}, " + "name = ${item.name}," + "data = ${item.data}" ) }) binding.itemImage.setOnClickListener { flag = !flag onItemClicked(flag) }
3. 使用 Fragment Result API獲取結果
從Fragment 1.3.0-alpha04版本開始
每個FragmentManager 都會實現 FragmentResultOwner
由FragmentB傳至FragmentA時,先在FragmentB設置監聽setFragmentResultListener()
以下為Android官網注意事項:
監聽器收到結果並觸發 onFragmentResult() 回調後,結果會被清除。這種行為有兩個主要影響:
a. 返回堆棧上的 Fragment 只有在被彈出且處於 STARTED 狀態之後才會收到結果。
b. 如果在設置結果時監聽結果的 Fragment 處於 STARTED 狀態,則會立即觸發監聽器的回調。
注意:由於 Fragment 結果存儲在 FragmentManager 級別,因此 Fragment 必須隨父 FragmentManager 一起附加到對 setFragmentResultListener() 或 setFragmentResult() 的調用。
主要是看setFragmentResult在哪個FragmentManager,調用時要特別注意使用級別
第一種:在Fragment之間傳遞結果

以下程式碼,由TestFragment2傳送資料至TestFragment
//在Activity中新增 val fragment = TestFragment() val fragment2 = TestFragment2() supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment ) } supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view2, fragment2 ) } //在TestFragment新增 setFragmentResultListener("requestKey") { requestKey, bundle -> val result = bundle.getString("bundleKey") println("result $result") } //在TestFragment2新增 binding.itemImage.setOnClickListener { val result = "result" setFragmentResult( "requestKey", bundleOf("bundleKey" to result) ) }
第二種:在父 Fragment 與子 Fragment 之間傳遞結果

以下程式碼,由TestFragment2(子)傳送資料至TestFragment(父)
因為子是childFragmentManager
所以接收的時候也需要使用childFragmentManager
//在Activity中新增 val fragment = TestFragment() supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment ) } //在TestFragment中新增 val fragment2 = TestFragment2() childFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment2 ) } childFragmentManager .setFragmentResultListener( "requestKey", viewLifecycleOwner ) { key, bundle -> val result = bundle.getString("bundleKey") println("result key $key, result $result") } //在TestFragment2中新增 binding.itemImage.setOnClickListener { val result = "result" setFragmentResult( "requestKey", bundleOf("bundleKey" to result) ) }
第三種:在Activity中接收結果
因為最終希望在Activity中接收資料
所以傳送資料需使用supportFragmentManager.setFragmentResult
//在Activity中新增 val fragment = TestFragment() supportFragmentManager.commit { setReorderingAllowed(true) add( R.id.fragment_container_view, fragment ) } supportFragmentManager .setFragmentResultListener( "requestKey", this) { requestKey, bundle -> val result = bundle.getString("bundleKey") println("result $result") } //在TestFragment中新增 binding.itemImage.setOnClickListener { val result = "result" activity?.supportFragmentManager?.setFragmentResult( "requestKey", bundleOf("bundleKey" to result) ) }
以上內容參考Android 官網
相關文章
Android Fragment建立、更換、尋找、Back Stack | Android Fragment add與replace分析 |
1. 使用XML在Activity與Fragment連接 2. 使用程式方式在Activity與Fragment連接 3. 將已新增的Fragment更換 4. 尋找已建立的Fragment 5. Fragment對應的FragmentManager 6. Back Stack使用 | |
Android Fragment 自定義constructor | Android Fragment show、hide、attach、detach用法 |
1. 重寫FragmentFactory 2. 建立Fragment 3. 在Activity中使用 | 1. 範例程式 2. Fragment show與hide是什麼呢? 3. Fragment attach與detach是什麼呢? |
Android Fragment Transitions動畫效果 | Android Fragment shared element transitions動畫效果 |
1. 範例程式 2. 功能介紹 | 1. 範例程式 2. 功能介紹 |
Android Fragment lifecycle | |
1. 簡略 2. 測試搭配setMaxLifecycle的生命週期 |