Android开发实现左右两侧两个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<ListBean> list;
public static class ListBean {
public int smallSortId;
public String smallSortName;
}
public boolean isSelected;
}

然后模拟构造一点数据:

        // 构造点数据,比如整个数据刚刚好就是从json转过来的,一个Bean里面有一个大类,有若干个小类
// 左侧的adapter就直接用这个构造好的list
for (int i = 0; i < 30; i++) {
SortBean bean = new SortBean();
bean.bigSortId = i;
bean.bigSortName = "大分类" + i;
List<SortBean.ListBean> list = new ArrayList<>();
for (int j = 0; j < 10; j++) {
SortBean.ListBean listBean = new SortBean.ListBean();
listBean.smallSortId = j;
listBean.smallSortName = "小标签" + j;
list.add(listBean);
}
bean.list = list;
mLeftList.add(bean);
}
// 右侧的list是将每一个大类和小类按次序添加,并且标记大类的位置
for (int i = 0; i < mLeftList.size(); i++) {
SortItem bigItem = new SortItem();
bigItem.viewType = ItemType.BIG_SORT;
bigItem.id = mLeftList.get(i).bigSortId;
bigItem.name = mLeftList.get(i).bigSortName;
// 标记大类的位置,所有项的position默认是-1,如果是大类就添加position,让他和左侧位置对应
bigItem.position = i;
mRightList.add(bigItem);
for (int j = 0; j < mLeftList.get(i).list.size(); j++) {
SortItem smallItem = new SortItem();
smallItem.viewType = ItemType.SMALL_SORT;
smallItem.id = mLeftList.get(i).list.get(j).smallSortId;
smallItem.name = mLeftList.get(i).list.get(j).smallSortName;
mRightList.add(smallItem);
}
}
// 点击左侧需要知道对应右侧的位置,用map先保存起来
for (int i = 0; i < mRightList.size(); i++) {
if (mRightList.get(i).position != -1) {
indexMap.put(mRightList.get(i).position, i);
}
}

左侧联动右侧

// 左侧列表的点击事件
leftAdapter.setOnItemClickListener(new SimpleRecyclerAdapter.OnItemClickListener<SortBean>() {
@Override
public void onItemClick(SortBean item, int index) {
// 左侧选中并滑到中间位置
leftAdapter.setSelectedPosition(index);
MyUtils.moveToMiddle(leftRecyclerView, index);
// 右侧滑到对应位置
((GridLayoutManager)rightRecyclerView.getLayoutManager())
.scrollToPositionWithOffset(indexMap.get(index),0);
}
});

左侧adapter的setSelectedPosition方法

// leftAdapter的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());
}
}
}
}

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

//右侧列表的滚动事件
rightRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//获取右侧列表的第一个可见Item的position
int topPosition = ((GridLayoutManager) rightRecyclerView.getLayoutManager()).findFirstVisibleItemPosition();
// 如果此项对应的是左边的大类的index
if (mRightList.get(topPosition).position != -1) {
MyUtils.moveToMiddle(leftRecyclerView, mRightList.get(topPosition).position);
leftAdapter.setSelectedPosition(mRightList.get(topPosition).position);
}
}
});

总结

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

源码地址:https://github.com/pengboboer/TwoRecyclerVIew

 

人已赞赏
Android文章

Android开发CountDownTimer轻松实现倒计时功能的方法

2020-2-1 9:04:21

Android文章

Android开发RecyclerView左右侧滑

2020-2-1 9:58:52

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索