我們很常遇到一個需求就是在不同的 Fragment 當中交換資料,通常解決的辦法有很多種,今天來試著紀錄到底有哪些方法可以實作?
這邊參考的來源是官方網站,其實官方網站給的資訊相當豐富,透過這些方法大致上已經涵蓋了所有能實作的方向。
透過 Fragment Result 的方式
一開始我們先宣告一個 Fragment,就叫它 MainFragment。
class MainFragment : Fragment() {
private lateinit var binding: FragmentMainBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = FragmentMainBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.goNext.setOnClickListener {
parentFragmentManager.beginTransaction()
.replace(R.id.container, NextFragment.newInstance())
.addToBackStack("")
.commit()
}
parentFragmentManager.setFragmentResultListener(REQUEST_KEY, viewLifecycleOwner) { _, bundle ->
val result = bundle.getString(BUNDLE_KEY, "")
Log.d("MainFragment", "result:$result")
}
}
companion object {
const val REQUEST_KEY = "requestKey"
const val BUNDLE_KEY = "bundleKey"
fun newInstance() = MainFragment()
}
}
這邊是拿來接收從另外一個 Fragment 所帶出來的結果。
因此我們這邊也需要設計另外一個 Fragment 來示範怎麼帶結果回來,就叫它 NextFragment。
class NextFragment : Fragment() {
private lateinit var binding: FragmentNextBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View {
binding = FragmentNextBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.goBack.setOnClickListener {
val result = "test"
setFragmentResult(MainFragment.REQUEST_KEY, bundleOf(MainFragment.BUNDLE_KEY to result))
parentFragmentManager.popBackStack()
}
}
companion object {
fun newInstance() = NextFragment()
}
}
如此一來就可以看到我們的 MainFragment 會收到結果。
透過共用 ViewModel 的方式
https://insert-koin.io/docs/reference/koin-android/viewmodel/#shared-viewmodel
Koin 是我用來操作 injection 的工具,透過這套 injection 的工具,可以輕鬆宣告出共通的 ViewModel 來協助我們使用通知 Fragment 頁面更新。
首先宣告一個 ViewModel 的 class。
class CommonViewModel : ViewModel() {
val isDataChanged = MutableLiveData<Boolean>()
}
接著在 AppModule 內加入一行 ViewModel 的宣告。
val vmModule = module {
viewModel { CommonViewModel() }
接著我們在 MainFragment 上面進行宣告。
class MainFragment : Fragment() {
private val commonViewModel by sharedViewModel<CommonViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
commonViewModel.isDataChanged.observe(viewLifecycleOwner, { isDataChanged ->
//do something
}
}
}
接著我們到 NextFragment 進行相關傳送資料。
class NextFragment : Fragment() {
private val commonViewModel by sharedViewModel<CommonViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
parentViewModel.isDataChanged.value = true
}
}
如此一來我們就可以在 MainFragment 收到資料變動了。
除了這樣還可以透過 EventBus、Singleton Pattern 製造一個 class 來進行傳送,不過這些方式可能耦合性會比較重,因此在使用時,必須小心斟酌設計。
參考網址
https://developer.android.com/guide/fragments/communicate