View的事件分发
事件分发:
public boolean dispatchTouchEvent(MotionEvent event)
View的默认实现没作分发(没有子view),作了些 clickable/enabled的校验后调用自己的onTouchEvent方法。
事件消费:
public boolean onTouchEvent(MotionEvent event)
如果可点击返回true,否则返回false。View的默认实现实现了点击和长按的手势识别,这就是为什么所有view都能设置点击和长按监听的原因.
ViewGruop的事件分发
事件分发:
public boolean dispatchTouchEvent(MotionEvent event)
ViewGroup可能有一到多个子view,典型的触摸事件都是由按下->一到多个移动事件->抬起组成,会在按下的时候做点击测试(是否在子控件区域内/绘制层级),找到消费了按下事件的targetview,后续事件都传递到这个targetview.
事件消费:
public boolean onTouchEvent(MotionEvent event)
ViewGroup默认没有重写View的事件消费逻辑,需要自定义手势处理的子view会重写这个方法.比如Scroolview的滚动实现.
拦截和反拦截
拦截(ViewGroup中才有):
public boolean onInterceptTouchEvent(MotionEvent ev)
以ScrollView为例,如果不做拦截,只要滚动布局中的子布局可点击,事件就会分发到子控件并被子控件消费,实现了滚动手势解析的ScrollView.onTouchEvent()就不会被调用.
为了消费触摸事件,ScrollView在onInterceptTouchEvent检测到滚动手势时会返回true,拦截后续的事件不再向下分发,而是交给自己的onTouchEvent处理. (被拦截的子控件此时会收到cancel事件)
禁止父容器拦截触摸事件(ViewGroup中才有):
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
主要用来处理手势冲突,比如可以水平滚动的viewpager中包含同样可水平滚动的子控件,此时子控件可以通过这个方法让自己优先消费手势.
TouchListener
手动依赖注入的方式设置事件处理逻辑(不需要继承View/ViewGroup来重写相关方法)
TouchDelegate
典型应用场景是用来扩大点击区域
事件消费优先级
TouchListener>TouchDelegate>onTouchEvent