事件分发机制的总结
为什么要了解Android事件分发机制?
问题一:内外嵌套的滑动冲突问题
不知大家有没有遇到过这样的情况,一个滚动的控件中再嵌套一个滚动的控件,发现当你要滚动里面的控件时,外面的控件滚动了,里面的控件却不滚动,或者,你要想让里面的控件滚动到底时,外面的控件才开始滚动.
问题二:方向滑动冲突问题
ListView的header放入一个左右滑动的banner或者说viewpager,那么当手指在banner上下滑动时将无法促使外层ListView的滑动,这就非常影响用户的使用体验了。
当了解了View的事件分发机制,以上问题将不再是问题了。 我们就可以利用事件分发机制来制定我们想要的滚动规则。
默认的情况下,当手指点击最中间的View1时,触发顺序如下(自上往下):
EVENT_DOWN:
activity1 - dispatchTouchEvent
viewGroup1 - dispatchTouchEvent
viewGroup1 - onInterceptTouchEvent
view1 - dispatchTouchEvent
view1 - onTouchEvent
viewGroup1 - onTouchEvent
activity1 - onTouchEvent
EVENT_MOVE:
activity1 - dispatchTouchEvent
activity1 - onTouchEvent
EVENT_UP:
activity1 - dispatchTouchEvent
activity1 - onTouchEvent
简单流程图
1、为什么EVENT_MOVE和EVENT_UP仅仅在最外层的activity1传递,而没有传递到viewGroup1和view1?
Android中事件分发机制中,如果内层的view都没有消耗事件,事件默认由最外层View进行处理,也就是activity处理事件。
2、什么时候view才算消费了事件?
第一种情况,view的clickable或者longClickable为true(普通view的clickable默认false);
第二种情况,view设置了点击监听或者长按监听,因为通过源码可以看到,setOnClickListener中调用了setClickable(true);
第三种情况,setOnTouchListener中返回true,这种也是最常见的。在setOnTouchListener中处理事件,然后返回true,那么该view的onTouchEvent将不会被执行。
3、viewGroup中的onInterceptTouchEvent在实际应用中起到什么作用?
举个例子,我们要自定义一个类似于listview的控件时,自定义完毕后,在使用过程中,发现我们自定义的listview控件中的item设置了button控件,button控件clickable为true,默认消费了事件,那么只要我们的手指触控在button上时,就不能对整个listview控件进行滑动了。
- 重写onInterceptTouchEvent方法就起到作用了,当手指滑动距离大于一定距离d时,我们就让onInterceptTouchEvent返回true,阻止事件往下传递。(tips:上面的距离d可以通过
ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context))获取)- 但是,如果我们把item中的button替换成listview呢。。那么,上面我们重写onInterceptTouchEvent之后,外层的listview拦截了事件以致于item中的listview无法滚动。这个很好解决,ViewGroup中有一个requestDisallowInterceptTouchEvent(true)方法,可以发出一次“禁止下一个事件的拦截”的请求,可以在我们的view的任何一个touch方法中调用view.getParent.requestDisallowInterceptTouchEvent(true)