左右两侧两个RecyclerView互相联动,右侧是网格的布局,写出最流畅的列表

概述

最近做项目需要用到两个RecyclerView互相联动的功能,类似美团外卖的点餐列表,不同的是项目用到的右侧是点击分类,要想写出流畅的列表,就一定要考虑得非常的周全,那么左侧一个RecyclerView,右侧一个RecyclerView,尽量减少嵌套,对于如下这种布局我们可以考虑使用GridLayoutManager,然后通过Recyclerview多布局和GridLayoutManager的一项占据多列来实现然后找了很多资料,把遇到的问题记录下来和大家一起分享,有类似需要的朋友可以看一下。先上图:

                   

需求:

1.左侧联动右侧,点击左侧任意一项、背景变色、右侧对应位置滚动到顶部。

2.右侧联动左侧,右侧滚动,左侧需要同步右侧在顶部的位置。

3.就像图中所示,点击图1的9,会向下移动到中间位置,点击图2的12,会向上移动到中间位置。

首先列表的数据,应该是从json解析出来的,格式大概是这样的:

public class SortBean {
    public int bigSortId;
    public String bigSortName;
    public List<SortItem> list;
    public boolean isSelected;

    public SortBean(int bigSortId, String bigSortName, List<SortItem> list) {
        this.bigSortId = bigSortId;
        this.bigSortName = bigSortName;
        this.list = list;
    }
}



public class SortItem {
    public int viewType;
    public int id;
    public String name;

    public int position = -1;

    public SortItem(int viewType, int id, String name, int position) {
        this.viewType = viewType;
        this.id = id;
        this.name = name;
        this.position = position;
    }
}

然后模拟构造一点数据:

public class Repository {

    private final List<SortBean> leftList = new ArrayList<>();
    private final List<SortItem> rightList = new ArrayList<>();
    // 记录左侧列表和右侧列表index对应关系
    private final Map<Integer, Integer> indexMap = new HashMap<>();

    public Repository() {
        buildList();
        buildIndexMap();
    }

    private void buildList() {
        for (int i = 0; i < 30; i++) {
            SortBean bean = new SortBean(i, "大分类" + i, getRightItemList(i));
            leftList.add(bean);
            rightList.addAll(bean.list);
        }
    }

    private void buildIndexMap() {
        for (int i = 0; i < rightList.size(); i++) {
            if (rightList.get(i).position != -1) {
                indexMap.put(rightList.get(i).position, i);
            }
        }
    }
    ...
    ...
}

左侧联动右侧

private void onClickLeftItem(int index) {
    // 左侧选中并滑到中间位置
    leftAdapter.setSelectedPosition(index);
    MyUtils.moveToMiddle(leftRecyclerView, index);
    // 右侧滑到对应位置
    ((GridLayoutManager)rightRecyclerView.getLayoutManager())
            .scrollToPositionWithOffset(repository.getIndexMap().get(index),0);
}

左侧adapter的setSelectedPosition方法

public void setSelectedPosition(int position) {
    mListData.get(mSelectedPosition).isSelected = false;
    notifyItemChanged(mSelectedPosition);
    mListData.get(position).isSelected = true;
    notifyItemChanged(position);
    mSelectedPosition = position;
}

RecyclerView移动到中间位置的方法

说一下大体思路吧,获得当前屏幕可见的第一项和最后一项,算出他们的中间值,通过getTop方法得到传入的position距离中间值的高度,然后调用scrollBy方法,注意在中间值的上方和下方要滑动的方向是不一样的。需要注意这里有一个注意事项,就是getChildAt方法无法获取超出屏幕外的position的项,如果index大于屏幕中可见的Item数会返回null,左侧并没有滑动的监听,比如左侧滑动到最底部,右侧还在第一项,右侧往上滑动会直接程序崩溃,那么如果index大于屏幕中Item的数量,我们直接使用ScrollToPosition。

// Reyclerview移动到中间位置的方法
public class MyUtils {
    
    public static void moveToMiddle(RecyclerView recyclerView, int position) {
        //先从RecyclerView的LayoutManager中获取当前第一项和最后一项的Position
        int firstItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition();
        int lastItem = ((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition();
        //中间位置
        int middle = (firstItem + lastItem)/2;
        // 取绝对值,index下标是当前的位置和中间位置的差,下标为index的view的top就是需要滑动的距离
        int index = (position - middle) >= 0 ? position - middle : -(position - middle);
        //左侧列表一共有getChildCount个Item,如果>这个值会返回null,程序崩溃,如果>getChildCount直接滑到指定位置,或者,都一样啦
        if (index >= recyclerView.getChildCount()) {
            recyclerView.scrollToPosition(position);
        } else {
            //如果当前位置在中间位置上面,往下移动,这里为了防止越界
            if(position < middle) {
                recyclerView.scrollBy(0, -recyclerView.getChildAt(index).getTop());
                // 在中间位置的下面,往上移动
            } else {
                recyclerView.scrollBy(0, recyclerView.getChildAt(index).getTop());
            }
        }
    }
}

右侧联动左侧,并将左侧移动到中间位置:

private void onClickRightItem(int index) {
    //获取右侧列表的第一个可见Item的position
    int topPosition = ((GridLayoutManager) rightRecyclerView.getLayoutManager()).findFirstVisibleItemPosition();
    // 如果此项对应的是左边的大类的index
    int currentPosition = repository.getRightList().get(topPosition).position;
    if (currentPosition != -1) {
        MyUtils.moveToMiddle(leftRecyclerView, currentPosition);
        leftAdapter.setSelectedPosition(currentPosition);
    }
}

总结

只贴出了关键的代码,实现起来还是很ok的,是我走了很多坑才写出来这么一个比较流畅的列表,我用的RecyclerView的Adapter和ViewHolder是继承了封装好的基类,如果有需要大家可以下载源码运行一下,看一下。

源码地址:https://github.com/pengboboer/two-recyclerview-link

后续

如果想学习RecyclerView的使用,推荐大家看鸿洋大神的博客,写的很详细了。

转载请标明出处: 
http://blog.csdn.net/lmj623565791/article/details/45059587; 
本文出自:【张鸿洋的博客】

 

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值