public boolean dispatchTouchEvent(MotionEvent event)
用来进行事件的分发。如果事件能够传递给当前 View,那么此方法一定会被调用,返回结果受当前 View 的 onTouchEvent()
和 下级 View 的 dispatchTouchEvent()
方法的影响,表示是否消耗当前事件。
public boolean onInterceptTouchEvent(MotionEvent ev)
用来判断是否拦截某个事件,如果当前 View 拦截了某个事件,那么同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
public boolean onTouchEvent(MotionEvent event)
在 dispatchTouchEvent()
中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前 View 无法再次接收到事件。
三者关系:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
当一个 View 需要处理事件时,如果它设置了 OnTouchListener,那么 OnTouchListener 中的 onTouch()
方法就会被回调。 这时事件如何处理还要看 onTouch()
的返回值,如果返回 false,则当前 View 的 onTouchEvent()
方法就会被调用;如果返回 true,那么 onTouchEvent()
不会被调用。在 onTouchEvent()
方法中,如果当前设置的有 OnClickListener,那么它的 onClick()
方法就会被调用。
点击事件的传递顺序:Activity -> Window -> View
如果一个 View 的 onTouchEvent()
返回 false,那么它的父容器的 onTouchEvent()
将会被调用。如果所有的元素都不处理这个事件,那么这个事件将会最终传递给 Activity 处理,即 Activiyt 的 onTouchEvent()
会被调用。
ViewGroup 默认不拦截任何点击事件
View 的 onTouchEvent()
默认都会消耗事件(返回 true),除非是不可点击的。(clickable 和 longClickable 同时为 false)
View 的 enable 属性不影响 onTouchEvent()
的默认返回值
通过 requestDisallowInterceptTouchEvent()
可以在子元素中干预父元素的事件分发。
子元素调用了 requestDisallowInterceptTouchEvent()
之后,父元素将无法拦截除了 ACTION_DOWN 以外的其他事件。为什么除了 ACTION_DOWN,原因是因为 ViewGroup 在分发时间的时候,如果是 ACTION_DOWN 就会 重置 FLAG_DISALLOW_INTERCEPT 这个标记位。因此,当面对 ACTION_DOWN 事件时,ViewGroup 总是会调用自己的 onInterceptTouchEvent()
方法来询问自己是否要拦截事件。
重写父容器的 onInterceptTouchEvent()
方法
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if (true) //如果父容器需要点击事件
intercepted = true;
else
intercepted = false;
break;
case MotionEvent.ACTION_UP:
break;
}
return intercepted;
}
重写子元素的 dispatchTouchEvent()
方法,并调用 requestDisallowInterceptTouchEvent()
方法
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (true) { //如果父容器需要点击事件
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}