Jetpack系列:
Lifecycle使用方法、原理分析 - 掘金 (juejin.cn)
LiveData使用方法、原理分析 - 掘金 (juejin.cn)
ViewModel使用方法、原理分析(一) - 掘金 (juejin.cn)
ViewModel使用方法、原理分析(二) - 掘金 (juejin.cn)
自定义View中获取ViewModel - 掘金 (juejin.cn)
假设有种业务场景,一个自定义View中包含很多数据、状态并且需要将这些数据状态进行保存,在旋转屏幕时数据不丢失,Jetpack组件中可以通过ViewModel进行实现,是否可以在自定义View管理数据呢?
在ViewModel源码分析中,回顾Activity、Fragment创建ViewModel过程如下:
//获取Activity、Fragment宿主中保存的ViewModel
val viewModel1 = ViewModelProvider(this)[CustomViewModel::class.java]
//在Fragment中获取宿主Activity中保存的ViewModel
val viewModel2 = activity?.let { ViewModelProvider(it)[CustomViewModel::class.java] }
在ViewModelProvider源码中发现,创建ViewModelProvider的常用2种方式:
public constructor(owner: ViewModelStoreOwner)
public constructor(owner: ViewModelStoreOwner, factory: Factory)
按照以上方式,只需要在自定义View中获取到ViewModelStoreOwner就可以创建ViewModel,在源码阅读的过程中发现有个ViewTreeViewModelStoreOwner,之前源码阅读时一直不理解这个类意义,那就分析源码:
在ViewTreeViewModelStoreOwner只有set()、get()方法,原理很简单,通过View的setTag()保存一个ViewModelStoreOwner对象,get()方法就是遍历View树中所有key为R.id.view_tree_view_model_store_owner的对象直到获取到对应的保存一个ViewModelStoreOwner对象
public class ViewTreeViewModelStoreOwner {
public static void set(@NonNull View view, @Nullable ViewModelStoreOwner viewModelStoreOwner) {
view.setTag(R.id.view_tree_view_model_store_owner, viewModelStoreOwner);
}
@Nullable
public static ViewModelStoreOwner get(@NonNull View view) {
ViewModelStoreOwner found = (ViewModelStoreOwner) view.getTag(
R.id.view_tree_view_model_store_owner);
if (found != null) return found;
ViewParent parent = view.getParent();
while (found == null && parent instanceof View) {
final View parentView = (View) parent;
found = (ViewModelStoreOwner) parentView.getTag(R.id.view_tree_view_model_store_owner);
parent = parentView.getParent();
}
return found;
}
}
再继续分析,源码中调用set()的时机,发现在ComponentActivity中有个initViewTreeOwners(),这不就是View所在Activity对象,也就是跟Activity、Fragment创建时所使用的ViewModelStoreOwner
private void initViewTreeOwners() {
ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
ViewTreeSavedStateRegistryOwner.set(getWindow().getDecorView(), this);
ViewTreeOnBackPressedDispatcherOwner.set(getWindow().getDecorView(), this);
}
这几个Tag是不是都很熟悉,在前几篇源码分析中都有介绍过,另外ViewTreeOnBackPressedDispatcherOwner是最新库中新加入的,主要为了适配Activity、Fragment处理返回事件混乱问题
那么在自定义View中就可以通过ViewTreeViewModelStoreOwner获取该自定义View所在宿主Activity,继续扒源码ViewTreeViewModelStoreKt.kt
public fun View.findViewTreeViewModelStoreOwner(): ViewModelStoreOwner? =
ViewTreeViewModelStoreOwner.get(this)
这样就可以试试在自定义View中如何创建ViewModel对象,在ViewTreeViewModelStoreOwner中get()方法中View时不停地向上遍历查询对应的ViewModelStoreOwner,所以需要在onAttachedToWindow()方法中执行创建方法
class WithViewModelCustomView: FrameLayout {
constructor(context:Context) : super(context) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initView(context)
}
private fun initView(context: Context){
LayoutInflater.from(context).inflate(R.layout.custom_fragment, this, true)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
val viewModel = findViewTreeViewModelStoreOwner()?.let {
ViewModelProvider(it)[CustomViewModel::class.java]
}
findViewTreeLifecycleOwner()?.let { lifecycleOwner ->
viewModel?.data1?.observe(lifecycleOwner){
findViewById<TextView>(R.id.tv1).text = it
}
}
}
}
通过以上方式就可以在自定义View中创建对应的ViewModel,保存Jetpack中的所有优势
再模仿Activity、Fragment ktx扩展函数中创建viewmodel方式,给View来个扩展函数:
@MainThread
public inline fun <reified VM: ViewModel> View.viewOfViewModel(): Lazy<VM> {
val viewModelStoreOwner = findViewTreeViewModelStoreOwner()!!
return ViewModelLazy(VM::class, {viewModelStoreOwner.viewModelStore}, {(viewModelStoreOwner as HasDefaultViewModelProviderFactory).defaultViewModelProviderFactory}, { CreationExtras.Empty})
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
// val viewModel = findViewTreeViewModelStoreOwner()?.let {
// ViewModelProvider(it)[CustomViewModel::class.java]
// }
// findViewTreeLifecycleOwner()?.let { lifecycleOwner ->
// viewModel?.data1?.observe(lifecycleOwner){
// findViewById<TextView>(R.id.tv1).text = it
// }
// }
val viewModel1 by viewOfViewModel<CustomViewModel>()
findViewTreeLifecycleOwner()?.let { lifecycleOwner ->
viewModel1.data1.observe(lifecycleOwner){
findViewById<TextView>(R.id.tv1).text = it
}
}
}