【Jetpack】学穿:ViewModel → 视图模型(中)

简介: 本节带来组件 → ViewModel 视图模型的解读!叫 视图数据 可能更贴切,有人也叫 视图状态

设置文本,预留点击接口而已,再接着到列表Fragment的布局,直接一个RecyclerView (fragment_list.xml)


<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="150dp"
    android:layout_height="match_parent">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>


接着自定义ViewHolder,维护一个选中的值,配置LiveData:


class SharedViewModel: ViewModel() {
    private val mSelectData = MutableLiveData<String>()
    fun select(data: String) { mSelectData.value = data }
    fun getSelected() = mSelectData
}


再接着把ListFragment也写出来,获取宿主Activity作用域的SharedViewModel实例,点击时更新值:


class ListFragment(data: ArrayList<String>): Fragment(R.layout.fragment_list) {
    private var mData = data
    // 定义SharedViewModel变量
    private var mModel: SharedViewModel? = null
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 获得宿主Activity作用域内的SharedViewModel实例
        mModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
        view.findViewById<RecyclerView>(R.id.rv_list).apply {
            adapter = ListAdapter(mData).also {
                it.setOnItemClickListener(object : ListAdapter.ItemClickListener {
                    override fun onItemClick(choose: String) {
                        // 更新ViewModel中的mSelectData
                        mModel?.select(choose)
                    }
                })
            }
            layoutManager = LinearLayoutManager(activity)
        }
    }
}


在接着到右侧Fragment,xml里就一个简单的TextView:(fragment_content.xml):


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light">
    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"/>
</LinearLayout>


补齐ContentFragment:


class ContentFragment : Fragment(R.layout.fragment_content) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 获取宿主Activity作用域的SharedViewModel实例,然后监听数据变化
        ViewModelProvider(requireActivity()).get(SharedViewModel::class.java).getSelected()
            .observe(viewLifecycleOwner) {
                view.findViewById<TextView>(R.id.tv_content).text = "您翻牌了:${it}"
            }
    }
}


紧着是测试Activity的xml (activity_vm_test.xml):


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <FrameLayout
        android:id="@+id/fly_choose"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
    <FrameLayout
        android:id="@+id/fly_content"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>


最后上测试Activity:


class VMTestActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_vm_test)
        val mData = arrayListOf("XX","XX","XX","XX","XXX", "XX", "XX", "XX")
        supportFragmentManager.apply {
            beginTransaction().replace(R.id.fly_choose, ListFragment(mData)).commit()
            beginTransaction().replace(R.id.fly_content, ContentFragment()).commit()
        }
    }
}


别看这里代码好像很多的样子,只是为了便于读者copy运行体验而已,核心就这四点:


  • ① 自定义定义ViewModel,里面放通信的数据,提供set()和get()方法;
  • ② ViewModelProvider(requireActivity()).get(SharedViewModel::class.java) 获取一个 作用域为宿主Activity 的ViewModel实例;
  • ③ 发消息:调用ViewModel实例的set()方法更新数据;
  • ④ 收消息:调用ViewModel实例的get()方法获得一个LiveData,然后observe() 监听数据变化


通过这样的方式,间接实现了跨页面通信,宿主Activity还蒙在鼓里 (无代码入侵),两个Fragment就偷偷完成了py交易,妙啊!!!


④ 作用域可控


上面两个Fragment轻松实现数据共享的例子,得益于ViewModel的 作用域可控,在创建 ViewModelProvider 时注入不同的 ViewModelStoreOwner 来反映作用域。

如果换成传入 Fragment实例本身,作用域就 仅限于此Fragment,当此Fragment销毁时,对应的ViewModel实例也会被销毁。


扩展一下:ViewModel是如何实现作用域可控的?


看一波源码探探原理,先跟下 ViewModelProvider 的构造方法:


网络异常,图片无法展示
|


网络异常,图片无法展示
|


初始化了一个Factory和ViewModelStore,先看第一个参数,调用了 owner.getViewModelStore()


网络异常,图片无法展示
|


此方法返回一个 ViewModelStore,跟下:


网络异常,图片无法展示
|


吼,内部 维护一个ViewModel的集合,还提供一个clear()方法,遍历回调ViewModel的clear()方法,并清空集合


所以包含关系是:ViewModelProvider  → ViewModelStoreViewModel

接着看第二个参数,根据传入的ViewModelStoreOwner不同,使用不同的工厂实例:


  • 判断条件:owner是否为 HasDefaultViewModelProviderFactory 类型?
  • 是:强转后调用 getDefaultViewModelProviderFactory() 获取一个  ViewModelProvider.Factory 实例;
  • 否:调用 NewInstanceFactory.getInstance() 获取一个 NewInstanceFactory 实例;


网络异常,图片无法展示
|


相关文章
|
4月前
|
前端开发 测试技术 API
Jetpack MVVM 七宗罪之六:ViewModel 接口暴露不合理
Jetpack MVVM 七宗罪之六:ViewModel 接口暴露不合理
74 0
|
2月前
|
XML 存储 API
Jetpack初尝试 NavController,LiveData,DataBing,ViewModel,Paging
Jetpack初尝试 NavController,LiveData,DataBing,ViewModel,Paging
|
编解码
Jetpack 之 ViewModel 组件介绍
ViewModel 是介于 View(视图)和 Model(数据模型)之间的一个东西。它起到了桥梁的作用,使视图和数据既能够分离开,也能够保持通信。
79 0
Jetpack 之 ViewModel 组件介绍
|
Android开发
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
142 0
|
存储 Android开发
Android JetPack组件之ViewModel的使用详解
Android JetPack组件之ViewModel的使用详解
109 0
|
存储 XML 前端开发
Android Jetpack系列之ViewModel
ViewModel的定义:**ViewModel旨在以注重生命周期的方式存储和管理界面相关的数据**。ViewModel本质上是视图(View)与数据(Model)之间的桥梁,想想以前的MVC模式,视图和数据都会写在Activity/Fragment中,导致Activity/Fragment过重,后续难以维护,而ViewModel将视图和数据进行了分离解耦,为视图层提供数据。
202 0
|
XML 存储 Android开发
大放光彩的安卓Jetpack组件-ViewModel(终)
前面我们已经说过Jetpack中ViewModel的作用、用法以及使用要点,但还缺少在Activity中的实例展示,所以本节我们将结合结果展示与代码进行解读,希望能更好的展示出ViewModel的风采。
127 0
|
前端开发 Android开发
大放光彩的安卓Jetpack组件-ViewModel(一)
在项目中,我遇到了一个问题,起因则是无法实时去获取信息来更新UI界面,因为我需要知道我是否获取到了实时信息
107 0
|
Android开发
大放光彩的安卓Jetpack组件-ViewModel(二)
上回我们说到使用方法,但没有具体去说明使用的要点,其实ViewMode还是挺容易上手的,这节我们来具体说明一些使用要点与运用方式。