Android Jetpack开发:原理解析与应用实战
上QQ阅读APP看书,第一时间看更新

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对象,此种方式不能用来实现数据共享功能。