有些需求要在一个页面里面加好多东西,于是会出现嵌套RecyclerView,ViewPager的情况。
但是在嵌套之后,即使使用NestedScrollView也会出现一些问题,比如嵌套RecyclerView会出现滑动卡顿,嵌套ViewPager会出现在ViewPager区域无法上下滑动的问题。
1.先来说一下嵌套ViewPager无法上下滑动的问题
我的思路是可以在dispatchTouchEvent方法里面,在ACTION_DOWN的时候,记录下y坐标;然后在ACTION_MOVE的时候,通过y坐标的差值,来决定是自己处理,还是交给父控件
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(ev.getY() - downY) > 50) {
getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
break;
}
这样通过事件分发,把事件拦截交给父控件,可以解决无法上下滑动的问题。
2.再来说说嵌套RecyclerView出现滑动卡顿的问题
我按照处理ViewPager的方法,想去解决RecyclerView的问题,结果发现这样不行,会产生好多问题。
然后查资料发现一个很简单的方法,覆写LinearLayoutManager的canScrollVertically方法,return false禁止竖直方向的滑动,试了一下果然可以,滑的很顺畅。
既然问题解决了,那么为什么同样的方法,可以解决ViewPager的问题,对RecyclerView却不行呢?
既然canScrollVertically方法可以解决,那么我想可以从源码中找canScrollVerticall方法的相关,就可以找出来答案了。就去看了一下源码,果然找到了答案。
源码那么多,怎么找呢?我想既然是触摸滑动的问题,那么应该逃不出分发(dispatchTouchEvent)、拦截(onInterceptTouchEvent)、处理(onTouchEvent)的那三个关于触摸的方法,就在RecyclerView里面搜索canScrollVertically,定位到dispatchTouchEvent方法里面,果然看到了
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
...无关代码省略...
switch (action) {
case MotionEvent.ACTION_DOWN:
...无关代码省略...
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
if (canScrollHorizontally) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
}
if (canScrollVertically) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
}
startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
break;
......
case MotionEvent.ACTION_MOVE: {
...无关代码省略...
if (mScrollState != SCROLL_STATE_DRAGGING) {
...无关代码省略...
}
} break;
...无关代码省略...
}
return mScrollState == SCROLL_STATE_DRAGGING;
}
原来在RecyclerView中,对滑动事件的方法,是在ACTION_DOWN中进行的。
那么如果自定义RecyclerView的话,只要在ACTION_DOWN中进行处理就应该可以了。
/**
* @author zhangyi
* @date 2018/9/21
* 解决嵌套RecycleView出现滑动卡顿的问题
*/
public class NestedRecycleView extends RecyclerView {
public NestedRecycleView(Context context) {
super(context);
}
public NestedRecycleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public NestedRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (e.getAction()== MotionEvent.ACTION_DOWN) {
return false;
}
return super.onInterceptTouchEvent(e);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction()== MotionEvent.ACTION_DOWN) {
//这个得加上,代表自己处理点击事件,不然点击item会没反应
return true;
}
return super.onTouchEvent(e);
}
}
看来对不同的View处理触摸事件,得具体分析,照搬可能是不行的