在 Fragment 之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。想象一下拆分视图 (master-detail
) Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
可以使用 ViewModel
对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel
来处理此类通信,如以下示例代码所示:
class SharedViewModel : ViewModel() { val selected = MutableLiveData<Item>() fun select(item: Item) { selected.value = item } } class MasterFragment : Fragment() { private lateinit var itemSelector: Selector // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) itemSelector.setOnClickListener { item -> // Update the UI } } } class DetailFragment : Fragment() { // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) model.selected.observe(viewLifecycleOwner, Observer<Item> { item -> // Update the UI }) } }
请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider
时,它们会收到相同的 SharedViewModel
实例(其范围限定为该 Activity)。
此方法具有以下优势:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了
SharedViewModel
约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。 - 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
当在其中一个fragment中修改了数据,希望另外一个fragment的数据也可以被修改。使用“共享 view model”的方式避免了一大堆炒鸡繁琐的回调接口。
class DatePickerFragment:DialogFragment() { private val crimeDetailViewModel:CrimeDetailViewModel by activityViewModels()//1设置为共享viewmodel override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { super.onCreateDialog(savedInstanceState) val dateListener = DatePickerDialog.OnDateSetListener{ _, year, month, dayOfMonth -> val chooseDate: Date = GregorianCalendar(year, month, dayOfMonth).time// val crime = crimeDetailViewModel.crimeLiveData.value crime?.date = chooseDate crimeDetailViewModel.saveCrime(crime!!)//2保存 } val calendar = Calendar.getInstance() calendar.time = crimeDetailViewModel.crimeLiveData.value?.date ?: Date()// val initYear = calendar.get(Calendar.YEAR) val initMonth = calendar.get(Calendar.MONTH) val initDay = calendar.get(Calendar.DAY_OF_MONTH) return DatePickerDialog( requireContext(), dateListener, initYear, initMonth, initDay ) } }
上面的代码实例中就使用了共享viewmodel.当然在另一个activity中也是用 by activityViewModels()这种方式初始化。
配合Navigation和livedata使用简直不要太丝滑!!!