JetPack之ViewModel初识|青训营笔记

135 阅读3分钟

JetPack之ViewModel初识|青训营笔记

这是我参与[第四届青训营]笔记创作活动的第6天

前言

对于四大组件我们简单的认识了一下,接下来就让我们开始正式的Android组件学习吧!!

首先,我们先介绍一下ViewModel,它是谷歌推荐的Jetpack中最重要的组件之一,它的出现,从根本上减轻activity所负责的任务,它专门存放于用户界面相关的一些数据,表明只要你在界面上看到的数据,它的相关变量都存放在ViewModel中。它的最强特性就是让数据可在发生屏幕旋转等配置更改后继续留存。接下来我们就来一点点探究它,首先我们得导入依赖项可参考⬇️

官方文档

ViewModel的生命周期

说道ViewModel的生命周期,那就一定得看这张图🔽

image-20220811154822173.png 上图就是activity在经历屏幕旋转后结束时所处的各个生命周期状态,旁边的就是ViewModel的生命周期了。大家可以发现,ViewModel的生命周期会比activity的生命周期来的更长,在感知到activity完成其任务后ViewModel才被清除,大家可以思考一下,它是怎么做到的❓

其实它是依靠着Lifecycle这一对象去感知其存在时间,而什么时间消失则是,Activity 完成时、 Fragment 分离时。进而影响着ViewModel的存在时间。对其感兴趣深究下去的同学可以参考官方文档,我就不再这过多说明了😆

ViewModel实现方式

接下来我们就来看看如何使用ViewModel为界面准备数据吧❗️

  • Activity

首先我们先简单创建好一个ViewModel的类

 class MainViewModel : ViewModel() {
   //A分数
     var scoreA = 0;
   //B分数
     var scoreB = 0;
 }
 //Activity
 class MainActivity : AppCompatActivity(),View.OnClickListener{
 ​
     private val TAG = "ViewModelActivity"
     private lateinit var binding:ActivityMainBinding
     lateinit var model: MainViewModel
 ​
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         binding = ActivityMainBinding.inflate(layoutInflater)
         val view = binding.root
         setContentView(view)
         //绑定viewModel
         model = ViewModelProvider(this)[MainViewModel::class.java]
         binding.btnReset.setOnClickListener(this)
         binding.btnThreeA.setOnClickListener(this)
         binding.btnThreeB.setOnClickListener(this)
         binding.btnTwoA.setOnClickListener(this)
         binding.btnTwoB.setOnClickListener(this)
         binding.aScore.text = "${model.scoreA}"
         binding.bScore.text = "${model.scoreB}"
     }
    override fun onClick(v: View?) {
         when(v){
             binding.btnThreeA -> {
                 model.scoreA = model.scoreA + 3
                 binding.aScore.text = "${model.scoreA}"
                 Log.d(TAG, "onCreateA: " + model.scoreA)
             }
             binding.btnThreeB -> {
                 model.scoreB += 3
                 binding.bScore.text = "${model.scoreB}"
                 Log.d(TAG, "onCreateB: " + model.scoreB)
             }
             binding.btnTwoA ->{
                 model.scoreA = model.scoreA + 2
                 binding.aScore.text = "${model.scoreA}"
                 Log.d(TAG, "onCreateA: " + model.scoreA)
             }
             binding.btnTwoB -> {
                 model.scoreB += 2
                 binding.bScore.text = "${model.scoreB}"
                 Log.d(TAG, "onCreateB: " + model.scoreB)
             }
             binding.btnReset ->{
                 model.scoreA = 0
                 model.scoreB = 0
                 binding.aScore.text = "${model.scoreA}"
                 binding.bScore.text = "${model.scoreB}"
                 Log.d(TAG, "onCreateB: " + model.scoreB)
                 Log.d(TAG, "onCreateA: " + model.scoreA)
             }
             else -> null
         }
     }
 }  

可以看到点击不同的按钮产生的数字变化如下

image-20220811201918955.png

接下来我们选择一下屏幕,可以知道屏幕旋转后,其显示的数字并未发生改变。

image-20220811202628395.png

  • Fragment

在activity中感受了ViewModel的魅力后,那接下来我们来说说fragment是如何去绑定viewModel的

 //绑定方式同activity
 val model  = ViewModelProvider(requireActivity())[MainViewModel::class.java]

image-20220811210623167.png

接着依旧旋转屏幕,效果跟旋转activity一样,没有差别,接着大家可多试试使用不同的fragment之间切换,来共享viewmodel的数据,可以参考官网,我就不再这里展示了

image-20220811212302741.png

向ViewModel传递参数

了解了ViewModel的特性之后,接下来我们就试着向viewModel传递参数吧

我们复用一下上面的MainViewModel,在其构造函数中添加两个参数,如下

 class MainViewModel(rescoreA:Int,rescoreB:Int) : ViewModel() {
   //A分数
     var scoreA = rescoreA;
   //B分数
     var scoreB = rescoreB;
 }

接下来就是向其中传递参数了,我们可以新建一个ViewModelProviderFactory类去实现ViewModelProvider.Factory接口

 class MainViewModelFactory(private val resoreA:Int,private val resoreB:Int) : ViewModelProvider.Factory {
     //实现接口方法,返回viewModel对象
     override fun <T : ViewModel?> create(modelClass: Class<T>): T {
         return MainViewModel(resoreA,resoreB) as T
     }
 }
 //接着就是去获取了,也跟上面差不多
 val viewModel = ViewModelProvider(this,MainViewModelFactory(resoreA = 1, resoreB = 2))[MainViewModel::class.java]

后面就靠大家自己去练习了,总体上大致相同,那大家可以思考一下,为啥要传递参数,它的应用场景在哪,其实有时候我们退出程序后,重新进入时,此时就可以调用这种方式来及时显示这些数据,不至于退出程序后,丢失。

最后

至此,viewModel就简单介绍完了,更详细的内容可参考一下官方文档,本文所写的是本人根据所参考资料,以及网上所学的个人见解,如有错误或者不恰当之处,欢迎私信我,加以改正!同时期待您的关注,感谢您的阅读,谢谢!

参考

ViewModel概览|安卓开发者