以下源码为4.4.2 不同版本源码区别较大
===========================View的dispatchTouchEvent==========================================
public boolean dispatchTouchEvent(MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
}
===========================主要看这两个语句==========================================
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
return false;
=====================================================================
dispatchTouchEvent ()
返回值的作用在上层view中体现
一般textview button等最上层的返回值表示是否消费了事件 等同于onTouchEvent
1.li != null 成立
2.li.mOnTouchListener != null 成立
3.(mViewFlags & ENABLED_MASK) == ENABLED 成立
4.li.mOnTouchListener.onTouch(this, event)
可以看到当调用了setOnTouchListenter 必然会执行onTouch方法
所以 可以知道前3个条件返回true(由&&的短路可知)
而第4个条件取决于onTouch的返回值
1.onTouch返回false 则第4个条件不成立 第1个if不执行 执行onTouchEvent(event)方法
2.onTouch返回true 则第一个if成立 dispatchTouchEvent直接返回true 不执行第二个if语句 也就不执行了onclick方法
=================================onTouchEvent====================================
public boolean onTouchEvent(MotionEvent event) {
.............
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
performClick();
break;
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_MOVE:
break;
}
return true;
}
return false;
}
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
=================================onTouchEvent====================================
imageview:
1.onTOuch---false onTouchEvent返回false dispatchTouchEvent返回false ontouch响应1次
2.onTouch---false setOnclickListener onTouchEvent返回true dispatchTouchEvent返回true ontouch响应2次 onclick响应1次
3.onTouch---true setOnclickListener onTouchEvent不执行 dispatchTouchEvent返回true ontouch响应2次 onclick不响应
button:
1.onTOuch---false onTouchEvent返回true dispatchTouchEvent返回true ontouch响应2次
2.onTouch---false setOnclickListener onTouchEvent返回true dispatchTouchEvent返回true ontouch响应2次 onclick响应1次
3.onTouch---true setOnclickListener onTouchEvent不执行 dispatchTouchEvent返回true ontouch响应2次 onclick不响应
分析:
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE))
button天生有点击事件(clickable=true)
所以button的onTouchEvent 必然返回ture 那么dispatchTouchEvent也true
imageview 天生不具有点击事件(clickable=false)
当没有为imageview.setOnClickListener 则if不成立 onTouchEvent 必然返回false 那么dispatchTouchEvent也返回false
当调用imageview.setOnClickListener 则if成立 onTouchEvent 必然返回true 那么dispatchTouchEvent也返回true
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
总结:
touch事件首先被onTouchEvent接收
如果onTouchEvent返回false(不消费) 则调用onclick询问是否消费 如果也不消费 则此次touch事件结束 后续ACTION_UP MOVE不再传递进来
如果onTouchEvent返回true(消费) 则不再调用onClick 后续ACTION_UP MOVE继续传递进来
再看:
=================================viewGroup的dispatchTouchEvent====================================
public boolean dispatchTouchEvent(MotionEvent ev) {
.......
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {//可以在子view中调用getParent.requestDisallowInterceptTouchEvent(boolean true)来屏蔽父view的onInterceptTouchEvent方法第爱哦用
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;
}
//通过onInterceptTouchEvent判断
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
.....
if (newTouchTarget == null && childrenCount != 0) {
....
//这里调用了child.dispatchTouchEvent()方法
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
mLastTouchDownIndex = childIndex;
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
//如果child.dispatchTouchEvent()返回false 那么newTouchTarget=null 条件成立
//此时touch事件并未被子view消费掉 所以调用自身的onTouchEvent事件看是否消费
//如果child.dispatchTouchEvent()返回true 那么newTouchTarget!=null 条件不成立
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
.......
return handled;
}
可以想像 当所有的onTouchEvent都返回false(不消费) 说明此次touch事件没有人要 后续的ACTION_DOWN,ACTION_MOVE当然不会再传递进来了
这里有个现实中的应用场景
需求:
在一个viewpager中嵌套viewpager 需要屏蔽外层viewpager的滑动事件
这里我们自定义一个viewpager 继承于v4的viewpager作为外层的viewpager 只添加如下改动
要想屏蔽外层的viewpager的滑动事件
在onTouchEvent中肯定不能调用viewpager的onTouchEvent事件(要不然就滑动了)
并且touch事件不在外层viewpager被消费
所以当setTouchMode为false时 onTouchEvent返回false
而为了保证内层的viewpager能响应滑动事件 在onInterceptTouchEvent中必须返回false (原viewpager的onInterceptTouchEvent会返回true 拦截touch事件)
注意:
onInterceptTouchEvent返回true之后并不会调用onTouchEvent方法
除非你自己重写onInterceptTouchEvent 方法 主动调用onTouchEvent方法