View的事件分发和处理

本文深入探讨了Android中View的事件分发、处理机制,重点分析了onTouchEvent、OnTouchListener及OnClickListener之间的交互作用。

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

事件分发,事件处理,事件拦截是一个比较蛋疼的地方,对于View的话,比ViewGroup相对要好些,View是没有事件拦截的,只有事件分发和处理;首先先看下正常情况下View的事件分发和处理会怎么样,在下面这三段代码中各打印一段log,看在touch的时候有哪些会打印出来;

自定义view的onTouchEvent:

   @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TAG","onTouchEvent--->"+event.getAction());
        return super.onTouchEvent(event);
    }

自定义view的OnTouchListener监听:

touchView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("TAG","onTouch--->"+event.getAction());
                return false;
            }
        });

自定义view的点击事件:

touchView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("TAG","onClick--->");
            }
        });

在touch完毕后,会看到
这里写图片描述
结果一:
onTouch(DOWN)—>onTouchEvent(DOWN)—>onTouch(MOVE)—>onTouchEvent(MOVE)—>onTouch(UP)—>onTouchEvent(UP)—>onClick

将自定义view的setOnTouchListener的返回值改成true;

touchView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("TAG","onTouch--->"+event.getAction());
                return true;
            }
        });

结果只有一个地方的log被打印出来了;
这里写图片描述
没错,不管你怎么touch只会打印onTouch处的log,想知道为什么,就一起去看看view的源码,在view源码中关于事件分发和处理有两个方法比较重要:

dispatchTouchEvent  事件分发
onTouchEvent   处理用户手势(DOWN,MOVE,UP等)

先看下dispatchTouchEvent 方法

   /**
     * Pass the touch screen motion event down to the 
     * target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, 
     * false otherwise.
     */
    public boolean 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;
            }
        }
        ...
        return result;
    }

这里看到有个ListenerInfo ,点进去看下;

static class ListenerInfo {

        protected OnFocusChangeListener mOnFocusChangeListener;

        private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;

        protected OnScrollChangeListener mOnScrollChangeListener;

        private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;

        public OnClickListener mOnClickListener;

        protected OnLongClickListener mOnLongClickListener;

        protected OnContextClickListener mOnContextClickListener;

        protected OnCreateContextMenuListener mOnCreateContextMenuListener;

        private OnKeyListener mOnKeyListener;

        private OnTouchListener mOnTouchListener;

        private OnHoverListener mOnHoverListener;

        private OnGenericMotionListener mOnGenericMotionListener;

        private OnDragListener mOnDragListener;

        private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;

        OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
    }

就是将view里面的一些事件全部放到了一个类中,看到dispatchTouchEvent中的这个if判断:

if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

如果ListenerInfo不为空,ListenerInfo中的OnTouchListener不为空,且Enabled为true;li.mOnTouchListener.onTouch(this, event)这里为true则result = true;否则result = false;

先说result = false的情况:
走完这个if,就会走到下面这if里面:

if (!result && onTouchEvent(event)) {
                result = true;
            }

result = false,!result就会true,就会去走onTouchEvent(event),

public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();
        ...
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        ...
                       }
                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            removeLongPressCallback();

                            if (!focusTaken) {
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }
                        ...
                    }
                    mIgnoreNextUpEvent = false;
                    break;
                case MotionEvent.ACTION_DOWN:
                    ...
                    break;
                case MotionEvent.ACTION_CANCEL:
                    ...
                case MotionEvent.ACTION_MOVE:
                    ...
                    break;
            }
            return true;
        }
        return false;
    }

在UP的时候会调用到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;
    }

在该方法中就会调用ListenerInfo中的OnClickListener,这样就会走到点击事件,也就是下面这个结果:
onTouch(DOWN)—>onTouchEvent(DOWN)—>onTouch(MOVE)—>onTouchEvent(MOVE)—>onTouch(UP)—>onTouchEvent(UP)—>onClick

result = true的情况:
resutl要为true的话,

if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

上面这个if中的li.mOnTouchListener.onTouch(this, event)返回值就要为true,也就是

touchView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.e("TAG","onTouch--->"+event.getAction());
                return true;
            }
        });

返回值为true;这个时候resutl为true,!result就会为false,那么下面这个代码就不会走,

if (!result && onTouchEvent(event)) {
                result = true;
            }

!result为false,onTouchEvent(event)就不会调用,并将result返回回去,onTouchEvent(event)没有被调用,performClick()肯定也就不会被调用了,所以不管你怎么touch都只会打印onTouch里面的log;

再看看自定义view中的onTouchEvent和dispatchTouchEvent这两个方法:

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("TAG","onTouchEvent--->"+event.getAction());
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

将onTouchEvent的返回值改为ture,运行看看什么效果;
这里写图片描述
并没有调用点击事件,这是因为返回true并没有去继承父类,不会去走父类中的onTouchEvent了,dispatchTouchEvent返回值改成true也是差不多的。上面如有写的不对的地方,欢迎交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值