关于ViewPager2内嵌Recycleview 并联动Tablayout 的坑


之前写了一篇 简单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即可完成点击事件监听