征服面试官系列:View的事件冲突,原理你了解吗?有怎样的解决方案?

本文深入探讨Android中View的事件分发机制,包括DOWN、MOVE和UP事件的处理流程,分析事件冲突的原因,并提供解决方案。事件分发从Activity开始,经PhoneWindow、DecorView,再到ViewGroup和View,涉及OnTouchListener和OnClickListener的协同工作。掌握事件分发,能有效解决UI交互中的冲突问题。

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

前面了解了【view的事件分发】,这里我们继续研究一下view的事件分发和处理,从而找到事件冲突的原因及其解决方案。

一、概念认知

在view的onTouchEvent(方法中)处理MotionEvent()的事件有:

        DOWN:  手指触摸屏幕的事件

        UP: 手指离开屏幕是的事件

        MOVE:   手指在屏幕上移动时的事件

        CANCEL: 当事件被拦截的时候就会触发

1)事件类型:

在view的onTouchEvent(方法中)处理MotionEvent()的事件有:

        DOWN:  手指触摸屏幕的事件

        UP: 手指离开屏幕是的事件

        MOVE:   手指在屏幕上移动时的事件

        CANCEL: 当事件被拦截的时候就会触发

2)事件分发的类与方法

    从源码角度来分析,事件分发的流程:

            activity--->dispatchTouchEvent()

            PhoneWindow --->superDispatchTouchEvent()

            DecorView --->superDispatchTouchEvent()

            ViewGroup--->dispatchTouchEvent()

            view--->dispatchTouchEvent()

            view--->onTouchEvent()
 

3)事件冲突: 

只有一个事件,但处理的对象有多个,而真正处理的对象并不是期待的对象,这样就发生了冲突;

也就是说,一个事件可以由多个view处理时会出现事件冲突(如滚动嵌套),而一个view对一个事件有多个处理方式时也会出现冲突(如touch 和click 事件)。

二、事件分发的流程

在学习事件分发之前,有些状态或标记是通过位运算符来运算的,所以下面先简单了解一下位运算符:

&  比较位的值都是1的时候,该位的结果才是1 
|  比较的位只要有一位是1 ,该位的结果就是1 
~  将当前位进行0 1 的交换,即0 换成1 ,1 换成0 

2.1)、DOWN 事件的处理与分发流程:

    ViewGroup.java中dispatchTouchEvent(MotionEvent ev) 的源码中

2.1.1)重置mGroupFlags

    执行的结果 mGroupFlags &= mGroupFlags & (~FLAG_DISALLOW_INTERCEPT);
 if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

//会重置mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;

2.1.2)事件是否拦截

    用上一步重置的mGroupFlags的值替换下面的表达式:

            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

    替换后的结果是:

        final boolean disallowIntercept = ( mGroupFlags & (~FLAG_DISALLOW_INTERCEPT)& FLAG_DISALLOW_INTERCEPT) != 0;

        由此可以判断disallowIntercept 为false; 就会调用 onInterceptTouchEvent(ev) 来判断是否被拦截,这个方法可在自定义view中被重写的。
 if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    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;
            }

2.1.3)事件处理与分发

1) 当被拦截的时候,即 onInterceptTouchEvent(ev)方法返回为true

A)  直接跳过下面的 if (!canceled && !intercepted)条件 ;

由于首次事件处理,那么mFirstTouchTarget==null成立,然后就进入if (mFirstTouchTarget == null) 里面执行,这时候就会进入到dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);

注意:第二个参数canceled 是false ,第三个参数是null

if (!canceled && !intercepted) {
    //此处忽略
 }

// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
       // No touch targets so treat this as an ordinary view.
       handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
       } else {
          //此处忽略
       }
}
B) 进入dispatchTransformedTouchEvent()方法:
 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
           //此处条件不成立,暂时忽略
        }
        
        // Perform any necessary transformations and dispatch.
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
           //此处条件不成立,暂时忽略
        }

        // Done.
        transformedEvent.recycle();
        return handled;    
 }

说明:

由于传入的第三个参数是null ,就会直接进入该方法的最底部,调用super.dispatchTouchEvent(transformedEvent); 而当前viewGroup的super是view;这样就很显

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值