双列表联动效果如下:
以上,是博主根据鸿洋大神的玩Android开放的API做的一个客户端,其中导航界面使用了双列表联动来展示数据。
1.使用强大的开源项目BRVAH装载两个列表的数据,简单易用,强烈推荐使用;
2.RecyclerView的滚动事件OnScrollListener研究 - 监听列表滚动状态的接口为 RecyclerView.OnScrollListener
注册接口的方法有两个:
1).setOnScrollListener(OnScrollListener listener) ,可能会有空指针问题,已经被废弃。
2).addOnScrollListener(OnScrollListener listener)
3.OnScrollListener类是个抽象类,有两个方法:
void onScrollStateChanged(RecyclerView recyclerView, int newState): 滚动状态变化时回调
recyclerView: 当前在滚动的RecyclerView
newState: 当前滚动状态 void onScrolled(RecyclerView recyclerView, int dx, int dy): 滚动时回调
recyclerView : 当前滚动的view
dx : 水平滚动距离
dy : 垂直滚动距离
dx > 0 时为手指向左滚动,列表滚动显示右面的内容
dx < 0 时为手指向右滚动,列表滚动显示左面的内容
dy > 0 时为手指向上滚动,列表滚动显示下面的内容
dy < 0 时为手指向下滚动,列表滚动显示上面的内容
4.RecycleView4种定位滚动方式演示
1)recyclerView.scrollBy(x, y),这个方法是自己去控制移动的距离,单位是像素;
2)recyclerView.scrollToPosition(position),这个方法的作用是定位到指定项,就是把你想显示的项显示出来,但是在屏幕的什么位置是不管的,只要那一项现在看得到了,那它就罢工了;
3)recyclerView.smoothScrollToPosition(position),smoothScrollToPosition(position)
和scrollToPosition(position)
效果基本相似,也是把你想显示的项显示出来,只要那一项现在看得到了,那它就罢工了,不同的是smoothScrollToPosition是平滑到你想显示的项,而scrollToPosition是直接定位显示;
4)((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(position,0);主角总是最后才登场,这种方式是定位到指定项如果该项可以置顶就将其置顶显示。比如:微信联系人的字母索引定位就是采用这种方式实现。
1.在布局中放入两个RecyclerView,左右各一个;
2.使用BRVAH开源项目,做好两个列表的适配器;
①右边的列表适配器:
public class RightAdapter extends BaseQuickAdapter {
public RightAdapter(int layoutResId) {
super(layoutResId);
}
@Override
protected void convert(BaseViewHolder holder, GuideBean.DataBean item) {
holder.getView(R.id.tv_item_tixi).setVisibility(View.GONE); // 右侧列表不为item设立标题
TagFlowLayout tfl = holder.getView(R.id.tfl);
ArrayList mVals = new ArrayList<>();
for (int i = 0; i < item.getArticles().size(); i++) {
mVals.add(item.getArticles().get(i).getTitle());
}
tfl.setAdapter(new TagAdapter(mVals) {
@Override
public View getView(FlowLayout parent, int position, String s) {
Random random = new Random();
int r = random.nextInt(150);
int g = random.nextInt(150);
int b = random.nextInt(150);
TextView tv = (TextView) LayoutInflater.from(mContext).inflate(R.layout.tfl, tfl, false);
tv.setTextColor(Color.rgb(r, g, b));
tv.setText(s);
return tv;
}
});
tfl.setOnTagClickListener((view, position, parent) -> {
Intent intent = new Intent(mContext, WebViewActivity.class);
intent.putExtra("link", item.getArticles().get(position).getLink());
mContext.startActivity(intent);
Log.d(TAG, "getView: " + item.getArticles().get(position).getLink() + "," + position);
return false;
});
}
}
②左边的列表适配器:
public class LeftAdapter extends BaseQuickAdapter {
private ArrayList selected = new ArrayList<>();
private int position;
public ArrayList getSelected() {
return selected;
}
public LeftAdapter(int layoutResId) {
super(layoutResId);
}
@Override
protected void convert(BaseViewHolder holder, GuideBean.DataBean item) {
holder.setText(R.id.tv_title, item.getName());
holder.setBackgroundColor(R.id.view, holder.getLayoutPosition() == position ? Color.parseColor("#ffffff") : Color.parseColor("#363636"));
}
public void setSelect(ArrayList booleans) {
this.selected = booleans;
notifyDataSetChanged();
}
public void setSelection(int pos) {
Log.d(TAG, "setSelection: " + pos);
this.position = pos;
notifyDataSetChanged();
}
}
3.使用OKGO请求网络数据,同时填充两个适配器:
private void getData() {
OKGO.get(Constants.NAVIURL, "navifragment", new StringCallback() {
@Override
public void onSuccess(Response response) {
GuideBean resultBean = JSONObject.parseObject(response.body(), GuideBean.class);
int errorCode = resultBean.getErrorCode();
if (errorCode == 0) {
articlesBeanList = new ArrayList<>();
dataBeanList = resultBean.getData();
for (int i = 0; i < dataBeanList.size(); i++) {
articlesBeanList.addAll(dataBeanList.get(i).getArticles());
}
leftAdapter = new LeftAdapter(R.layout.item_text);
rv1.setAdapter(leftAdapter);
leftAdapter.setNewData(dataBeanList);
rightAdapter = new RightAdapter(R.layout.item_tixi);
rv2.setAdapter(rightAdapter);
if (dataBeanList != null) {
rightAdapter.setNewData(dataBeanList);
}
... ...
@Override
public void onError(Response response) {
super.onError(response);
}
@Override
public void onFinish() {
super.onFinish();
}
});
}
4.写入双列表联动的控制;
①点击左边列表,右边列表进行滑动:
leftAdapter.setOnItemClickListener((adapter, view, position) -> {
leftAdapter.setSelection(position);
LinearLayoutManager layoutManager = (LinearLayoutManager) rv2.getLayoutManager();
... ...
//根据左侧,定位右侧的展示数据
layoutManager.scrollToPositionWithOffset(position, 0); // 定位到某个item,并将其置顶显示
});
②滑动右侧列表,左侧列表进行联动:
rv2.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//获取滚动时的第一条展示的position
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition();
int position = 0;
for (int i = 0; i < firstVisibleItemPosition; i++) {
position += dataBeanList.get(i).getArticles().size();
}
//获取右侧数据的关联id
GuideBean.DataBean.ArticlesBean articlesBean = articlesBeanList.get(position);
int outId = articlesBean.getChapterId();
//记录外部id, 更新左侧状态栏状态
int pos = 0;
for (int i = 0; i < dataBeanList.size(); i++) {
int id = dataBeanList.get(i).getCid();
if ((outId == id)) {
pos = i;
}
}
leftAdapter.setSelection(pos);
rv1.smoothScrollToPosition(pos);
}
});
}
这里比较关键,首先我们需要获取右侧列表第一个Item项的位置,然后获取它的一个索引值,与左侧列表的索引值进行比较,看看某个属性会相等,相等则说明,右侧列表项归属于某个左侧列表项。这个属性由鸿洋大神提供的API返回的JSON数据可知为:
左侧列表属性:cid,一个int类型的值;
右侧列表属性:chapterId,一个int类型的值。
只要两个值相等,则判定左侧的列表需要进行滑动。
另外推荐一个已开源的双列表联动的项目:
Linkage-RecyclerView