调用ViewTreeViewModelStoreOwner.get(view)返回null,如何获取列表项单独ViewModelStoreOwner?
问题根因
为什么你拿到的是null
ViewTreeViewModelStoreOwner返回的值依赖视图挂载到Window树的状态,你在控件初始化(构造函数、onFinishInflate)阶段调用时,视图还未完成attach,必然返回null- 普通View和RecyclerView的Item视图本身默认没有实现
ViewModelStoreOwner接口,就算你在attach后调用,拿到的也只是最近的上层父节点(也就是你现在用的Fragment)的实例,所以所有列表项共用同一个ViewModel。
解决方案(实现每个列表项独立ViewModel)
核心思路是给每个RecyclerView.ViewHolder实现ViewModelStoreOwner接口,每个ViewHolder持有独立的ViewModelStore,实现每个列表项单独的ViewModel生命周期。
实现步骤
- 实现带ViewModelStore的ViewHolder
class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), ViewModelStoreOwner { // 每个ViewHolder独有一个ViewModelStore override val viewModelStore: ViewModelStore = ViewModelStore() fun bind(itemData: YourItemDataType) { // 配合Hilt获取ViewModel实例 val viewModelFactory = EntryPointAccessors.fromApplication( itemView.context.applicationContext, HiltViewModelFactoryEntryPoint::class.java ).hiltViewModelFactory() val itemViewModel = ViewModelProvider( this, viewModelFactory )[YourItemViewModel::class.java] // 后续视图绑定逻辑 } // 对外暴露回收清理方法 fun clear() { viewModelStore.clear() } }
- 定义Hilt EntryPoint获取ViewModel工厂
@EntryPoint @InstallIn(SingletonComponent::class) interface HiltViewModelFactoryEntryPoint { fun hiltViewModelFactory(): HiltViewModelFactory }
- 在适配器中处理ViewHolder回收逻辑
override fun onViewRecycled(holder: ItemViewHolder) { holder.clear() super.onViewRecycled(holder) }
其他注意事项
- 如果只需要获取父Fragment的ViewModelStoreOwner,把调用逻辑移到自定义控件的
onAttachedToWindow回调中即可拿到非空值:
override fun onAttachedToWindow() { super.onAttachedToWindow() val parentViewModelStoreOwner = ViewTreeViewModelStoreOwner.get(this) // 后续逻辑 }
- 不要在自定义控件内部维护独立的ViewModelStore,放在ViewHolder中更适配RecyclerView的回收机制,避免出现内存泄漏。
内容的提问来源于stack exchange,提问作者Tosin John
相关产品推荐
相关产品推荐

