dispatchTouchEvent分发机制

以下源码为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)


以上4个条件 
可以看到当调用了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


public void setOnClickListener(OnClickListener l) {
        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方法





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值