之前写了一篇 简单ViewPager2内嵌Recycleview 并联动Tablayout
本以为已经实现的差不多了,确实从效果上来看,也差不多了。但是一个报错引起了我的注意,在滑动的过程中,不断看到
Any method call that might change the structureof the RecyclerView or the adapter contents should be postponed tothe next frame.
这个提示,滑动一次警告一次。百度了一下,大致是因为由于数据改变,recycleview本身就已经在刷新列表,而我又调用了notifyDataSetChanged,导致了recycleview在上一次刷新还没完成的情况下,又要进行下一次刷新,所以抛出的错误。
这个错误本身其实并不会发生,因为recycleview是监听数据变化的,所以也不用再去调用notify
然而如果不调用的话,会发现数据显示错乱了,这是为什么呢?
数据混乱
二级adapter 纵向数据混乱
这是因为recycleview的holder自动复用导致的,由于没有对recycleview设置ID或者是viewtype,recycleview就默认对holder进行复用,所以数据混乱,搞清楚了原因,我们只需要在MenuAdapter中进行ItemViewType的设置,就可以帮助recycleview混乱复用。
@Override public int getItemViewType(int position) { return mMenuList.get(position).cateID; }
一级adapter 横向数据混乱
这是由于viewpager2的adapter使用错误导致的,当我们在使用二级嵌套的时候,在给二级adapter嵌套的时候,我们应该在创建对象的时候初始化adpater,并且给所有子recycleview赋值同一个adapter,这样就能保证数据不混乱
public MenuViewPagerAdapter(Context context, List<MenuItem> menuList) { this.context = context; this.mMenuList = menuList; menuAdapterData = new ArrayList<>(); menuAdapter = new MenuAdapter(menuAdapterData); }recyclerView.setAdapter(menuAdapter);
静态赋值
而在上一篇文章中,我们的数据是通过获取后再赋值给adapter的,属于动态赋值。如果我们是打包好的数据,也可以一次性赋值给viewpager的adapter,然后由它来给每个子recycleview进行列表赋值,只需要暴露一个方法
public void setCateID(int cateID) { this.cateID = cateID; menuAdapterData.clear(); menuAdapterData.addAll(initData()); menuAdapter.notifyDataSetChanged(); }
并且在需要切换cate列表的地方调用即可,initData是进行数据筛选
点击事件
在recycleview中,默认并没有直接的点击事件监听器,而这种二级嵌套的recycleview,更是无法直接使用api,这就需要我们自己对view的点击事件实现监听。
二级adapter
首先我们对最里层的adapter进行 implements View.OnClickListener
接着就可以在onClick中定义点击事件,position通过tag提前写入来获取
@Override public void onClick(View v) { int position = (int) v.getTag(); mOnItemClickListener.onItemClick(v, mMenuList.get(position), position); }
接着定义接口
public interface OnItemClickListener { void onItemClick(View view, MenuItem menuItem, int position); //void onItemLongClick(int position); }
最后暴露方法setmOnItemClickListener
void setmOnItemClickListener(MenuAdapter.OnItemClickListener onItemClickListener) { this.mOnItemClickListener = onItemClickListener; }
一级adapter
在一级adapter中,我们同样定义接口
public interface OnItemClickListener { void onItemClick(View view, MenuItem menuItem); //void onItemLongClick(int position); }
暴露方法
public void setmOnItemClickListener(MenuViewPagerAdapter.OnItemClickListener onItemClickListener) { this.mOnItemClickListener = onItemClickListener; }
最重要的是在onBindViewHolder中,对点击事件进行关联
menuAdapter.setmOnItemClickListener((v, menuItem, position1) -> mOnItemClickListener.onItemClick(v, menuItem));
这样我们就把点击事件从里面暴露到了外面
最后只需要在声明一级adapter的地方,进行setmOnItemClickListener即可完成点击事件监听