Android控件事件分发原理

一、事件分发简介

android核心机制,用户点击屏幕控件的时候,指尖触碰到控件,会有按下(down)、移动(move)、抬起(up)操作,这一系列操作简称为事件,看上去简单,实际上包含了复杂的事件分发逻辑。我们知道一个activity里面有viewgroup、还有view,viewgroup里面有各种view,view与view甚至还会重叠,那么系统是如何判断用户点击的是哪个控件呢?

二、事件分发原理

这里涉及到三个核心的方法:
事件分发函数:override fun dispatchTouchEvent(ev: MotionEvent?): Boolean
事件执行函数:override fun onTouchEvent(event: MotionEvent?): Boolean
事件拦截函数:override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean
其中在activity中,没有事件拦截函数,因为默认根布局的事件肯定要往下分发,不需要拦截,下面会讲到,activity的分发会分发到window,然后到viewgroup;
在view中,没有事件拦截函数,因为没有子view,不需要往下传递事件;
viewgroup没有事件执行函数。

用户点击屏幕整个流程
1.调用activity的dispatchTouchEvent(),查看源码,调用到了window.dispatchTouchEvent()

public boolean dispatchTouchEvent(MotionEvent ev) {
   
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
   
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
   
            return true;
        }
        return onTouchEvent(ev);
    }

这个简单易理解的吧,window层或者更下的viewgroup或者view消费了,那么返回true,表明整体消费了,如果最终发现子view没有一个愿意消费的,那么直接自己来,自己做touchEvent。为什么我说了"window层或者更下的viewgroup或者view消费了",因为后面讲解源码的时候会发现,其实dispatchTouchEvent这个函数包含了ontouchevent和onintercepttouchevent,且在里面还会有对子view的各种遍历,只要子view消费了,就最终返回给自己所有父布局dispatchTouchEvent=true 表明消费。

window这里实际上就是PhoneWindow,查看源码,调用了mDecor,熟悉源码的知道实际上mDecor就是activtiy 的跟布局,是一个不能设置大小(默认全屏)的FrameLayout

fun superDispatchTouchEvent(event: MotionEvent?): Boolean {
   
        return mDecor.superDispatchTouchEvent(event)
    }

ok,实际上mDecor.superDispatchTouchEvent(event)这个代码调用到了viewgroup.dispatchTouchEvent了。

2.调用viewgroup.dispatchTouchEvent,二话不说,我们直接看源码,这里省略部分代码,主要看主要的逻辑,重点逻辑标记了“核心”tag的注释,其实逻辑很简单,先判断一下需不需要拦截onInterceptTouchEvent,如果不需要拦截,就对子view遍历循环,继续调用dispatchTouchEvent,如果递归下去发现子view没有了,那么走最终调用view.dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {
   
        //...
        boolean handled = false;   //核心:是否消费的标记位
        if (onFilterTouchEventForSecurity(ev)) {
      //校验一下事件是否合理安全,可以忽略
        
            //...

            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
   
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
   
                    //核心:判断事件是不是需要分发给子view,注意,只有down事件时候判断一次,后面不会二次判断,所以事件down的时候被拦截了,后续所有事件都不会分发给子view了
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); 
                } else {
   
                    intercepted = false;
                }
            } else {
   
                intercepted = true;
            }

            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            final boolean isMouseEvent = ev.getSource() == InputDevice.SOURCE_MOUSE;
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0
                    && !isMouseEvent;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {
   
                //核心:如果事件没拦截也没有取消,开始对子view开始分发
                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;

                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
   
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C_lea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值