MVVMCross Android中如何实现MvxRecyclerView嵌套并解决子适配器报错
嘿,我之前在MVVMCross项目里做过类似的日期分组商品列表,刚好碰到过子适配器报错的问题,咱们一步步来排查解决:
1. 先检查子RecyclerView的布局绑定是否踩了低级坑
首先得确认你子Item布局里的MvxRecyclerView绑定没写错——特别是ItemsSource要和父ItemViewModel里的集合属性对应上,还有ItemTemplate的布局ID别打错了。举个正确的例子:
<MvxRecyclerView android:id="@+id/rv_child_products" android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="ItemsSource PurchasedProducts" local:MvxItemTemplate="@layout/item_purchased_product" />
要是ItemsSource误绑成父ViewModel的属性了,那肯定出问题。
2. 子适配器的构造和ViewHolder创建必须守MVVMCross的规矩
你继承了MvxRecyclerAdapter,那构造函数一定要接收IMvxAndroidBindingContext,而且OnCreateViewHolder里必须用绑定上下文来Inflate布局,绝对不能直接用系统的LayoutInflater!我之前就是踩了这个坑,导致绑定失效还报错。正确的子适配器写法参考:
public class ChildProductAdapter : MvxRecyclerAdapter { public ChildProductAdapter(IMvxAndroidBindingContext bindingContext) : base(bindingContext) { } public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { // 重点:用BindingContext加载布局,保证绑定上下文不丢失 var itemView = BindingContext.BindingInflate(viewType, parent, false); return new ChildProductViewHolder(itemView, BindingContext); } }
要是你直接用LayoutInflater.From(parent.Context)加载布局,MVVMCross的绑定系统根本认不出这个布局,不报错才怪。
3. 父适配器里初始化子适配器的时机很重要
别在父适配器的OnCreateViewHolder里初始化子适配器!因为ViewHolder会被复用,重复创建适配器不仅浪费性能,还会导致绑定上下文混乱。正确的做法是在OnBindViewHolder里给当前的父Item绑定子数据源:
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { base.OnBindViewHolder(holder, position); var dateGroupViewModel = GetItem(position) as DateGroupViewModel; var dateViewHolder = holder as DateGroupViewHolder; // 用父适配器的绑定上下文创建子适配器,保证上下文传递正确 var childAdapter = new ChildProductAdapter(BindingContext); childAdapter.ItemsSource = dateGroupViewModel.PurchasedProducts; dateViewHolder.ChildRecyclerView.Adapter = childAdapter; }
这样每次绑定父Item的时候,子适配器都会拿到当前分组的正确数据源。
4. 让MVVMCross自动创建子适配器(懒人福利)
要是你嫌手动初始化子适配器麻烦,也可以直接在父Item的布局里指定子适配器的类型,让MVVMCross自动处理:
<MvxRecyclerView android:id="@+id/rv_child_products" android:layout_width="match_parent" android:layout_height="wrap_content" local:MvxBind="ItemsSource PurchasedProducts" local:MvxItemTemplate="@layout/item_purchased_product" local:MvxAdapter="YourApp.Droid.Adapters.ChildProductAdapter, YourApp.Droid" />
这种方式能减少手动代码的出错概率,毕竟MVVMCross对自己的组件绑定更熟。
5. 抓具体错误日志才是最快的排查方式
要是上面的步骤都试了还是报错,那你得抓OnCreateViewHolder里的具体错误信息。比如加个try-catch打日志:
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { try { var itemView = BindingContext.BindingInflate(viewType, parent, false); return new ChildProductViewHolder(itemView, BindingContext); } catch (Exception ex) { // 用MVVMCross的日志工具打印错误 Mvx.IoCProvider.Resolve<IMvxLog>().Error(ex, "Failed to create child product view holder"); throw; } }
看具体是NullReference(比如BindingContext为null),还是InflateException(布局ID错了),这样就能精准定位问题。
最后补个小提示:嵌套RecyclerView记得给子RecyclerView加android:nestedScrollingEnabled="false",避免滚动冲突,用户体验会好很多。
内容的提问来源于stack exchange,提问作者KP_

