前两篇文章:
可能是最详细的Android点击事件处理详解
可能是最详细的Android点击事件处理详解(二)
这里再次延伸一下,在ScrollView和RecyclerView嵌套中touch事件的传递过程,以及嵌套滑动冲突的问题。
![](https://github.com/wangxp423/BlogImages/raw/master/blogimage/scroll_recycler.png)
如上图,外层是一个NestedScrollView,上半部分是一个400dp的RecyclerView。下面是用来填充的一些TextView。
本篇主要分四块来讲述:
- 滑动上半部分的RecyclerView
- 滑动下半部分的NestedScrollView
- NestedScrollView内部只有RecyclerView滑动没有Fling效果的情况
- 处理上半部分RecyclerView滑动到底部,焦点在RecyclerView上滑动没有Fling效果的情况
一,滑动上半部分的RecyclerView
日志如下:
TouchScrollViewActivity[dispatchTouchEvent, 64]: ACTION_DOWN
NormalScrollView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalScrollView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalRecyclerView[dispatchTouchEvent, 36]: ACTION_DOWN
NormalRecyclerView[onInterceptTouchEvent, 63]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 111]: ACTION_DOWN
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 67]: ACTION_MOVE
NormalView[dispatchTouchEvent, 96]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 122]: ACTION_CANCEL
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 94]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 94]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 72]: ACTION_UP
NormalScrollView[dispatchTouchEvent, 42]: ACTION_UP
NormalRecyclerView[dispatchTouchEvent, 43]: ACTION_UP
NormalRecyclerView[onTouchEvent, 97]: ACTION_UP
首先说下一下只有400dp高度的RecyclerView在滑动的时候是有Fling效果的。因为高度的限定,焦点在RecyclerView内,而且RecyclerView是可以滑动的,此时虽然ScrollView也是可以滑动,但是优先处理内部滑动,并且也有Fling效果,不存在冲突问题。因为系统已经帮我们处理了。
从日志看跟我们可能是最详细的Android点击事件处理详解(二)
中滑动日志是一样的。只不过又多了一层ScrollView而已。这里不做过多描述,重点在于此时的RecyclerView有Fling效果。
二,滑动下半部分的NestedScrollView
日志如下:
TouchScrollViewActivity[dispatchTouchEvent, 64]: ACTION_DOWN
NormalScrollView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalScrollView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 111]: ACTION_DOWN
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 89]: ACTION_MOVE
NormalView$2[onTouch, 61]: ACTION_MOVE
NormalView[onTouchEvent, 115]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalView[dispatchTouchEvent, 96]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 122]: ACTION_CANCEL
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onTouchEvent, 92]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 72]: ACTION_UP
NormalScrollView[dispatchTouchEvent, 42]: ACTION_UP
NormalScrollView[onTouchEvent, 95]: ACTION_UP
首先描述一下此时的手指焦点是在ScrollView下面的View上面的此时滑动的是ScrollView是有Fling效果的。
从日志看跟我们可能是最详细的Android点击事件处理详解(二)
中滑动日志是一样的。只不过是将RecyclerView换成了ScrollView。这里不做过多描述,重点在于此时的ScrollView有Fling效果。因为内部的View不具备滑动效果。不存在冲突。
三,NestedScrollView内部只有RecyclerView滑动没有Fling效果的情况
日志如下:
TouchScrollViewActivity[dispatchTouchEvent, 64]: ACTION_DOWN
NormalScrollView[dispatchTouchEvent, 35]: ACTION_DOWN
NormalScrollView[onInterceptTouchEvent, 61]: ACTION_DOWN
NormalRecyclerView[dispatchTouchEvent, 36]: ACTION_DOWN
NormalRecyclerView[onInterceptTouchEvent, 63]: ACTION_DOWN
NormalView[dispatchTouchEvent, 84]: ACTION_DOWN
NormalView$2[onTouch, 57]: ACTION_DOWN
NormalView[onTouchEvent, 111]: ACTION_DOWN
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onInterceptTouchEvent, 67]: ACTION_MOVE
NormalView[dispatchTouchEvent, 96]: ACTION_CANCEL
NormalView$2[onTouch, 68]: ACTION_CANCEL
NormalView[onTouchEvent, 122]: ACTION_CANCEL
TouchScrollViewActivity[dispatchTouchEvent, 68]: ACTION_MOVE
NormalScrollView[dispatchTouchEvent, 39]: ACTION_MOVE
NormalScrollView[onInterceptTouchEvent, 65]: ACTION_MOVE
NormalRecyclerView[dispatchTouchEvent, 40]: ACTION_MOVE
NormalRecyclerView[onTouchEvent, 94]: ACTION_MOVE
TouchScrollViewActivity[dispatchTouchEvent, 72]: ACTION_UP
NormalScrollView[dispatchTouchEvent, 42]: ACTION_UP
NormalScrollView[onInterceptTouchEvent, 69]: ACTION_UP
NormalRecyclerView[dispatchTouchEvent, 43]: ACTION_UP
NormalRecyclerView[onTouchEvent, 97]: ACTION_UP
此时的滑动日志其实和RecyclerView限制高度。滑动RecyclerView的日志一样,但是因为此时的ScrollView内部只有一个RecyclerView此时的ScollView和RecyclerView高度其实是一样的。此时系统在滑动处理的时候,没有帮助我们处理,此时滑动的时候从日志看是作用到内部的RecyclerView了,但是此时的RecyclerView没有Fling效果,即快速上滑屏幕,RecyclerView只会动一点,而不是惯性滚动很多。
对于这种情况其实很好解决,因为ScrollView和RecyclerView高度一样。滑动内层和外层都是可以的。而一般处理冲突在外层处理比较方便一点。这里又三种处理方式。
- 在ScrollView层的onInterceptTouchEvent去拦截Move事件(这里需要对一些细微move做一些处理,不然不容易出发touch到内层的事件),不让内层的RecyclerView接收滑动事件,直接滑动ScrollView即可,此时在滑动就可以有Fling效果了。
- recyclerView.setNestedScrollingEnabled(false);可以屏蔽RecyclerView的滑动,直接让外层的ScrollView滑动。
- 重写LinearLayoutManager下的canScrollVertically()让他返回false禁止竖向滑动。
以上三种方法均是屏蔽内层的滑动,让外层进行滑动。
四,处理上半部分RecyclerView滑动到底部,焦点在RecyclerView上滑动没有Fling效果的情况
首先我们知道,单独滑动ScrollView和RecyclerView都是可以有Fling效果的,但是当RecyclerView和ScrollView同时滑动的时候,就不行了。这个时候我们要做的就是区分他们的滑动,因为系统以及帮我们处理了一部分。我们需要做的只是在临界点去让他们分别滑动。
提供如下思路:
- 在外层ScrollView的onInterceptTouchEvent去拦截move事件
- 当焦点在RecyclerView时,去判断内层RecyclerView是否滑动到底部了。如果没有滑动到底部,我们要让它去滑动内层的RecyclerView。但是外层拦截了Move。我们需要调用getParent().requestDisallowInterceptTouchEvent(true);去驳回外层的Move拦截,让他不去拦截Move。这个时候就可以滑动内层RecyclerView
- 当RecyclerView滑动到底部的时候,这个时候如果焦点还在RecyclerView滑动就不会有Fling效果,我们调用getParent().requestDisallowInterceptTouchEvent(false);不去驳回外层拦截,让他执行ScrollView的滑动。这个时候滑动ScrollView就有Fling效果了。
思路是这样的。肯定可以。只不过有一些细节需要处理。代码后续我会补上。
项目源码: https://github.com/wangxp423/ViewExercise
在,首页 -> Touch事件研究中。