概述
最近做项目需要用到两个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;
本文出自:【张鸿洋的博客】