zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

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

Jetpack 模型 视图 ViewModel
2023-09-27 14:25:56 时间
本节带来组件 → 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 的构造方法


image


image


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


image


此方法返回一个 ViewModelStore 跟下


image


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


所以包含关系是 ViewModelProvider → ViewModelStore → ViewModel

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


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


image



Android Jetpack系列之ViewModel ViewModel的定义:**ViewModel旨在以注重生命周期的方式存储和管理界面相关的数据**。ViewModel本质上是视图(View)与数据(Model)之间的桥梁,想想以前的MVC模式,视图和数据都会写在Activity/Fragment中,导致Activity/Fragment过重,后续难以维护,而ViewModel将视图和数据进行了分离解耦,为视图层提供数据。
大放光彩的安卓Jetpack组件-ViewModel(终) 前面我们已经说过Jetpack中ViewModel的作用、用法以及使用要点,但还缺少在Activity中的实例展示,所以本节我们将结合结果展示与代码进行解读,希望能更好的展示出ViewModel的风采。
大放光彩的安卓Jetpack组件-ViewModel(二) 上回我们说到使用方法,但没有具体去说明使用的要点,其实ViewMode还是挺容易上手的,这节我们来具体说明一些使用要点与运用方式。