说说Android上的事件传递

本文详细解析了Android中事件从父View到子View的传递过程,包括dispatchTouchEvent、onInterceptTouchEvent等方法的作用及调用流程。同时,对比了OnTouchListener与onClickListener的区别,解释了为何onTouch返回true时onClick不会被执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前写过一篇事件传递相关的博客

一句话总结一个事件从父View到子View的传递:

dispatchTouchEvent是告诉你的领导(父VIew)这件事是不是交给你来做,返回true,交给你;返回false,这事我不管……你去找别人。无论这件事归不归你管,领导问你能不能做的时候,你总要说句话吧?所以当一个ViewGroup或者View接受到事件时dispatchTouchEvent总会被调用。onInterceptTouchEvent是告诉你的手下的小弟(该View的子View)刚才领导交的差事是你亲自做,还是小弟们做,你亲自做返回true,然后调用你自己的onTouchEvent,返回false,交给小弟做。

上面说的是一个完整的事件,是从 down->move->move->up 整个事件。

这次说说OnTouchListener和onClickListener。
在View.java->dispatchTouchEvent(MotionEvent event)方法中,有这样一段代码

if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //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;
            }
        }复制代码

如果设置了OnTouchListener,这里就会调用OnTouchListener的onTouch方法,并且返回true(告诉父View,这个事件我来处理)。
如果没有设置OnTouchListener,那么往下看,调用了onTouchEvent(event),如果返回true,dispatchTouchEvent就返回true。

从这里可以知道,OnTouchListener.onTouch是优先于onTouchEvent(event)的。如果OnTouchListener.onTouch返回true(消耗了事件),那么result就等于true,后面if (!result && onTouchEvent(event))中的onTouchEvent(event)就不会走了。而我们常用的onClickListener就是在onTouchEvent(event)中被调用的。所以onTouch返回true,onClick就不会被执行的原因就在这里。

根据上面的代码发现,onClick要被执行是需要很多步判断,可谓是历经千难万阻。但是实际的编程中,我们只要给View添加一个onClickListener就行了,这是怎么做到的?请往下看

在onTouchEvent(MotionEvent event)的代码的MotionEvent.ACTION_UP的事件下,有这样一段代码

if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                .....
                if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
                        ...
                        return true;
    }复制代码

这里可以发现为什么onClick和onLongClick不会冲突。
这里调用了performClick(),在这之前做了一些线程安全的工作。onclick()就是在performClick()中被执行的。

public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }复制代码

通过代码发现,一定要满足if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) 这个条件,这里才会被调用。而且onTouchEvent会返回true。

而当我们绑定监听器的时候,这个View会被设成clickable = true

public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }复制代码

这就是“我们只要给View添加一个onClickListener就行了”的原因了。绑定了监听器,onTouchEvent会返回true,dispatchTouchEvent就返回true。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值