RecycrollView嵌套RecyclerView!
大家可能或多或少都遇到过这种需求,也有很多种情况需要处理多个RecyclerView(或ListView)共同使用。这时就需要用到ScrollView来嵌套RecyclerView,其实也很简单,需要重写一下RecyclerView的LayoutManager,将所有条目的高度计算出来就好了,但是这种处理方式会影响RecyclerView的回收机制,所以不建议大面积使用。关于该解决方案的代码,网上一搜一大把,我就不浪费时间了。
这里我们主要说一下RecyclerView嵌套ScrollView或者NestedScrollView的场景和滑动冲突处理吧。
RecyclerView嵌套ScrollView呢?
RecyclerView嵌套ScrollView或NestedScrollView,可能有很多人没有听过这种需求,因为我在网上搜了很多关于嵌套的内容,都是ScrollView嵌套RecyclerView,根本没有RecyclerView嵌套ScrollView的。真尴尬,这里我就整理一下我的解决方法。
有这样一个需求,首先是一个RecyclerView列表,Grid模式,卡片显示样式,在卡片内部需要显示一个列表数据,如果数据超出卡片显示范围需要滑动,这里就需要用到一个RecyclerView 或者 用ScrollView包裹现行布局让他滚动。我的实现思路就是使用ScrollView+LinearLayout的方式实现滚动,这样就会出现两个问题。
第一个问题,就是卡片内部数据无法滑动,这个问题就是我们今天主角。其实解决起来很简单,先看代码:
/**
* 禁止父容器及上层容器拦截事件
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
super.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
super.requestDisallowInterceptTouchEvent(true);
break;
}
return super.dispatchTouchEvent(ev);
}
这就解决问题了,原理呢,就是在View树中,ScrollView的父容器以及上层容器在执行onIntercepTouchEvent的时候将事件拦截处理了。这是RecyclerView中的处理:
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
...
switch (action) {
case MotionEvent.ACTION_DOWN:
...
if (mScrollState == SCROLL_STATE_SETTLING) {
getParent().requestDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
}
...
startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
break;
...
case MotionEvent.ACTION_MOVE: {
...
if (mScrollState != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
boolean startScroll = false;
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
mLastTouchX = x;
startScroll = true;
}
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
mLastTouchY = y;
startScroll = true;
}
if (startScroll) {
setScrollState(SCROLL_STATE_DRAGGING);
}
}
} break;
...
}
return mScrollState == SCROLL_STATE_DRAGGING;
}
这样就导致了在滑动时内层的ScrollView无法触发滑动。在看看刚才的解决方案,在事件分发的时候就先禁止其父容器和上层容器拦截事件,这样外层的RecyclerView就无法拦截滑动事件,内层的ScrollView或者NestedScrollView就可以自由滑动了。
/**
* 禁止父容器及上层容器拦截事件
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//这里不要调用自己的requestDisallowInterceptTouchEvent
//一定要调用父类的 具体原因大家可以探究。
super.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_UP:
super.requestDisallowInterceptTouchEvent(true);
break;
}
return super.dispatchTouchEvent(ev);
}
说完第一个问题了,该说第二个问题了,那就是ScrollView的无法点击问题了。。这个问题网上有很多解决方案,这里我就不一一介绍优缺点了。只介绍一种方案:
@Override
public boolean canScrollVertically(int direction) {
return true;
}
并且给ScrollView的直接子布局设置一个点击事件,来响应Item的点击事件。
RecyclerView嵌套RecyclerView呢?
关于RecyclerView嵌套RecyclerView 的就比较多了,外层纵向,内层横向,只需要给内层RecyclerView设置一个高度就可以了,这是因为RecyclerView在onLayout的时候处理的问题。
还有就是多个RecyclerView同向嵌套的问题,这种需求目前应该有吧,其实也很简单两种方案,第一种就是让内部Recyclerview可以滚动,给定一个固定高度,让他滚动,解决方案就是上面的方案。第二种就是内部的RecyclerView完全展开,不过这种方案我还是不建议大家用,因为复用不了,用RecyclerView还有什么意义呢。还不如ScrollView呢。
关于RecyclerView的嵌套问题,就先写到这,欢迎大神们留言,讨论。