3.4 使用ViewModel实现数据共享
在实际开发中,经常会遇到两个Fragment之间有通信的需求,假设现在有AFragment和BFragment,这两个Fragment中都有滑动的标签,我们想要让两个Fragment的标签选项实现同步滑动,比如:在AFragment中选中了“新闻”标签,切换到BFragment时也会自动切换到“新闻”标签。一般情况下实现这个需求的方式有两种:
- 在某个Fragment中选中数据时将选择的标签位置记录下来,当切换Fragment时,取出当前记录的位置进行切换。
- 通过为宿主Activity增加实现接口的方式进行通信。
上面是开发者经常使用的两种方式,现在使用ViewModel的特性便可以很简单地解决这个问题。
在ViewModel中创建一个currentPosition变量,用于记录当前选中的位置,并提供设置变量的方法以供外部Fragment调用,ViewModel的主要代码如下:
class ShareDataViewModel : ViewModel() { private var currentPosition: Int = 0 fun getCurrentPosition(): Int { return currentPosition } fun positionChanged(currentPosition: Int) { this.currentPosition = currentPosition } }
这里可能会有读者疑惑,为什么要将currentPosition定义为私有变量,并且单独提供设置和获取currentPosition的方法?为什么不直接定义为public属性,这样在外部就可以直接调用了。这是因为理论上开发者应将所有类变量的操作都放在类的内部,遵循基本的设计原则,如果将类的属性设为公共属性暴露给外部,则无法保证数据的统一性和完整性。
言归正传,在AFragment中,当标签变化的时候设置currentPosition的值,在BFragment获取值后更新UI代码,AFragment的主要代码如下:
class AFragment : Fragment() { lateinit var shareDataViewModel: ShareDataViewModel override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) shareDataViewModel = ViewModelProvider(requireActivity()).get(ShareDataViewModel::class.java) ... //标签选项发生变化 shareDataViewModel.positionChanged(position) ... } }
BFragment的主要代码如下:
class BFragment : Fragment() { lateinit var shareDataViewModel: ShareDataViewModel override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) shareDataViewModel = ViewModelProvider(requireActivity()).get(ShareDataViewModel::class.java) ... //获取当前选中的标签位置 shareDataViewModel.getCurrentPosition() updateUI() ... } }
如此一来,使用ViewModel组件就实现了同一宿主Activity下不同Fragment之间的数据共享功能。
注意
在Fragment中通过ViewModelProvider获取ViewModel对象时,如果参数是requireActivity(),则获取的是宿主Activity对应的ViewModel对象。此种获取方式可以用来实现数据共享。如果参数是this,则获取的是Fragment各自对应的ViewModel对象,此种方式不能用来实现数据共享功能。