View 的dispatchTouchEvent 与 onTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
......
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
......
return result;
}
- View 是没有 onInterceptTouchEvent的 。当事件传递给它时 。必定会触发它的 dispatchTouchEvent ,也必定触发它的 onTouchEvent 。
- mOnTouchListener 的处理优先级高于 onTouchEvent
- mOnTouchListener 返回 true , 则不会调用 onTouchEvent 。
- View 的 dispatchTouchEvent 返回值 和 mOnTouchListener 以及onTouchEvent 有关。
View onTouchEvent 逻辑如下:
public boolean onTouchEvent(MotionEvent event) {
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
......
return true;
}
return false;
- View clickable == true时 ,任何事件默认都返回True 。 clickable == false 时,任何事件都返回false 。可以通过 xml 中设置clickable 属性,也可以通过代码设置 Listener 。有的控件默认是可点击的如:Button 。 TextView 默认clickable 为false。
ViewGroup 的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(intercepted || mFirstTouchTarget == null){
// ViewGroup 将自己的角色定位成View ,用View 的dispatchTouchEvent ,自己处理事件
consume = super.dispatchTouchEvent(event);
} else if(mFirstTouchTarget != null){
consume = mFirstTouchTarget.dispatchTouchEvent(ev);
}
return consume ;
}
关于拦截 :
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
注意到下面那个 intercepted = true; 也就是 ViewGroup 它 很想拦截 。具体情况有以下几种。
- ACTION_DOWN ,也就是刚开始一个事件序列, 子View们 请求disallowIntercept 或者ViewGroup 没有重写 onInterceptTouchEvent 。 这时 子View 们是有 机会处理 Down 事件的。
- ACTION_MOVE , 此时 mFirstTouchTarget == null 。也就是说没有子View 对
Down 事件 返回 true。 那么ViewGroup 也甭判断了 。 直接拦截。 - ACTION_MOVE , 此时 mFirstTouchTarget != null 。有子View 的dispatchTouchEvent(ACTION_DOWN) 返回 true 。 那么 又进入到了权利角斗的环节了,View 是否允许ViewGroup 拦截 ,允许则 ViewGroup 判断是否拦截。
拦截后会发生什么:
- 单指触摸下,会将mFirstTouchTarget 置为空
- ViewGroup 自己处理 MotionEvent ,也就是调用 自身的 onTouchEvent 。
- 拦截ACTION_MOVE ,会发送 Cancel 事件给 上一个mFirstTouchTarget 。
- 当ViewGroup 拦截事件后,子View 再也没有 处理事件的机会了。(很纯的责任链模式啊!!!)
正是由于 父ViewGroup 拦截后,子View 再没有机会处理后续的触摸事件,就导致了Android 处理事件的局限性,也为后面推出 NestScoll 机制埋下了伏笔。
View 对 ACTION_MOVE返回false
当一个View 或者 ViewGroup 能够接触到 Action_MOVE 的时候,就代表 它的dispathTouchEvent 对 ACTION_DOWN 返回了 true 。 此时 mFirstTouchTarget 已经不为 null 了,就算 对 ACTION_MOVE返回false ,也不会影响到 它接收后续的事件。 不过返回 false 的 ACTION_MOVE 事件会 交给 Activity 处理。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
Log.e("touch_tag"," MainActivity " + MotionEvent.actionToString(event.getAction()) + " result = " +result);
return result;
}
}