android的touch事件分发机制-View篇

转载请注明出处:http://write.blog.csdn.net/mdeditor#!postId=47418837

先赞一下Markdown编辑器,很好用:)

网上讲解touch事件分发的文章已经很多了,看得多了,更觉得混乱。所以打算自己写一篇,顺理一下,也希望对后来者能有点用处。

在具体讲解之前,我想先回答几个常见的问题。
1. 为什么我们要了解touch的分发机制
2. touch的事件分发涉及到哪几个方法
3. view和viewgroup的分发机制一样吗

我们先来看一段代码:

TextView view = (TextView)findViewById(R.id.view_touch);
view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(null, "OnClickListener");
            }
        });
view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Log.e(null, "OnTouchListener action=" + motionEvent.getAction());
                return true;
            }
        });

在这段代码里,我声明了一个TextView,然后注册了一个新的touch事件的监听器,运行结果如下:
08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0
08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1
08-11 16:14:16.600 27057-27057/wanmei.com.svgtest E/﹕ OnClickListener

这个比较简单,我想大家都知道:
action=0指的是ACTION_DOWN事件,action=1指的是ACTION_UP事件,因为我点击的比较快,手指也没有移动,所以并没有触发ACTION_MOVE事件(action=2)
而Click事件在touch事件之后执行了

现在,我们把onTouch方法的返回值改成true,输出会怎么样呢:
08-11 16:14:16.380 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=0
08-11 16:14:16.575 27057-27057/wanmei.com.svgtest E/﹕ OnTouchListener action=1

可以看到,在触发了touch事件后,并没有触发Click事件.

这是为啥?
先普及一个问题:在你点击一个view的时候,其实并不是直接就走onTouch了(虽然看起来是你注册了一个touch的监听器并实现了这个接口)。
实际上,在你点击以后,先走的是dispatchPointerEvent这个方法(此方法在view类里可以看到):

public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

这个方法具体干啥用的,有兴趣的童鞋可以研究一下,因为不在咱们这篇的研究范围内,就不赘述了。
但是大家可以看到,这个方法的return值有两种,当event.isTouchEvent()的时候,调用了dispatchTouchEvent方法。

这个dispatchTouchEvent方法才是咱们关心的。它负责当view捕获到touch事件时,分发事件:

   public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        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;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

代码比较长,但是咱们不需要都看明白,单独看下面这段:

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;
            }
        }

我来解释一下:
首先通过onFilterTouchEventForSecurity判断当前view是否被遮挡,然后定义了一个ListenerInfo的实例。
ListenerInfo这个类是View的内部类,有兴趣的童鞋可以看看源码,里面定义了各种监听器,包括onclick,onlongclick什么的。
然后重点来了,在判断了一堆是不是null,可不可用之后,最终还是调用到了li.mOnTouchListener.onTouch(this, event)
也就是我们在文章最初提到的onTouch方法。
而且,在这里我们还用到这个onTouch的返回值(这里其实用到了4个返回值,li肯定是不为null了,li.mOnTouchListener是不是null取决于你有没有setOnTouchListener,(mViewFlags & ENABLED_MASK) == ENABLED取决于你的这个view控件是不是enable的),只有当onTouch返回true的时候,result才等于true,也就是dispatchTouchEvent才返回true。
那么dispatchTouchEvent返回true or false有什么意义呢?咱们来看源码的注释:
@return True if the event was handled by the view, false otherwise.
当返回true的时候,表明事件已经被view被处理掉了,false的时候反之。

我们来理一下思路:
当你触摸屏幕的时候,view最先做的是分发这个触摸事件,所以不管咋样,进来给dispatchTouchEvent分发,然后呢,在做了一堆判断后,分发给了onTouch。
而onTouch在做了处理之后,返回了一个布尔值。这个布尔值将决定dispatchTouchEvent是否继续分发这个触摸事件。
所以在文章的开头,如果你的onTouch返回了true,明显dispatchTouchEvent就不会再分发给onClick了

ps:dispatchTouchEvent代码里还有一个onTouchEvent,这个方法是activity的,有机会我会写一篇单独说明。
pps:写的比较浅,看到csdn里有很多写的比我深的多的,只希望我这篇对于初学者更易懂一些:)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值