Android开发Android技术知识Android开发经验谈

BAT 高级工程师进阶必修之 View 的事件分发机制

2022-10-11  本文已影响0人  程序老秃子

前言

View 在 Android 中是一个非常重要的概念,作用堪比四大组件,所谓 View 的事件分发,就是对 MotionEvent 事件的分发过程,当一个 MotionEvent 产生了以后,系统需要把这个事件传递给一个具体的 View,这个过程就是事件分发

View 的虽称不上 Android 四大组件,但它的重要性可以说是跟四大组件平级,根据使用频率,甚至比广播内容提供器重要;view 主要包含两类:ViewGroup 和具体的 View,有 Android 开发经验的都知道这两类的区别了,我们时时刻刻都有使用到 view,例如: TextView、ImageView 等,正因为我们时时刻刻都在用,所以就显得特别重要了

事件分发过程由三个很重要的方法共同来完成:

当一个触摸事件产生后,他的传递过程遵循如下顺序:Activity->Window->View,即事件总是先传递给 Activity,Activity 再传递给 Window,最后再传给顶级 View,顶级 View 接收到事件后,会按照分发机制向下继续分发

顺着箭头走,可以看到事件是被

例如: LinearLayout,事件来到这里就会稍微复杂起来了,会根据情况处理事件

刚刚分析到 ViewGroup 的时候,当 disallowIntercept 为 false 时,事件会来到它的 onInterceptTouchEvent(ev),这个方法的从名字可以看出是否拦截事件的意思,返回 true 表示拦截,则会交给自己的 onTouchEvent(ev)方法,返回 false 表示不拦截事件,将事件丢给了 View 的 dispatchTouchEvent(MotionEvent event),也就相当于 disallowIntercept 等于 true

前面 ViewGroup 说到的 disallowIntercept 变量是通过 parent.requestDisallowInterceptTouchEvent(boolean)设置的,设置是否禁止拦截事件的意思,一般子 view 不希望父类拦截事件的话可以调用该方法,在处理滑动冲突的时候会经常用到这个方法,他的优先级也是最高的

接下来用代码是示例:

Activity 对事件的分发
public boolean dispatchTouchEvent(MotionEvent ev) {
        ...

        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

Activity 的事件的处理其实并不负责,即如果下层(不管是 ViewGroup 还是 View )消耗了这个事件,那么 if 语句就为 true , 则 dispatchTouchEvent 就返回 true ;如果没有消耗就自己对事件进行处理,即调用 onTouchEvent 方法

public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }
/** @hide */
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        final boolean isOutside =
                event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
                || event.getAction() == MotionEvent.ACTION_OUTSIDE;
        if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
            return true;
        }
        return false;
    }

Activity 的 onTouchEvent 会对这个事件进行判断,如果事件在窗口边界外就返回 true,dispatchTouchEvent 就返回 true ;如果在边界内就 返回 false ,最后 dispatchTouchEvent 也会返回 false

View 对事件的分发

这里先说 View 对事件的分发是因为 ViewGroup 继承自 View ,ViewGroup 对事件的分发会调用到父类(也就是View )的方法,因此先理清 View 的分发有助于理解

public boolean dispatchTouchEvent(MotionEvent event) {
           ...
            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;
    }

可以看到 View 的事件的处理是先判断 mOnTouchListener !=null 和 View 设置 ENABLED 这两个条件成不成立,不过成立则 调用 onTouch 方法,且如果 onTouch 返回了 true ,那个事件就被消耗 ,View 的 dispatchTouchEvent 就返回 true ; 相反,如果条件不成立或者 onTouch 返回 false ,那么就会执行 View 的 onTouchEvent 方法

public boolean onTouchEvent(MotionEvent event) {  
     ...
     final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;


    // 若可点击,包括LONG_CLICKABLE 或者 CLICKABLE
    if (((viewFlags & CLICKABLE) == CLICKABLE ||  
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  

                switch (event.getAction()) { 


                    case MotionEvent.ACTION_UP:  
                        boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;  

                            ...

                            // 执行performClick() 
                            performClick();  
                            break;  


                    case MotionEvent.ACTION_DOWN:  
                        ...
                        break;  


                    case MotionEvent.ACTION_CANCEL:  
                        ...
                        break;


                    case MotionEvent.ACTION_MOVE:  
                        ...
                        break;  
                } //>> 若可点击,就返回true
                return true;  
            }  //>> 若不可点击,就返回false
            return false;  
        }
public boolean performClick() {  

        if (mOnClickListener != null) {  
            playSoundEffect(SoundEffectConstants.CLICK);  
            mOnClickListener.onClick(this);  
            return true;  
        }  
        return false;  
    }

在 onTouchEvent 方法中,如果 View 是可点击的,比如设置了 onClick 或者 onLongClick ,就会执行 onClick 方法,并且 onTouchEvent 返回 true

小结

这篇文章,其实不难;主要是将 View 的事件分发机制以及开发当中经常用到的一些知识点,总结了一下

最后这里放上我耗时两个月,将自己8年 Android 开发知识笔记整理成了一份系统学习资料笔记技术相关的知识点笔记中都有详细的解读并且把每个技术点整理成了 PDF 文档(知识脉络 + 诸多细节)有需要的小伙伴:可点击此处查看获取方式,或者简信发送"笔记"就可以免费领取

BAT 高工必修- View 绘制

BAT 高工必修- View 事件分发机制

以上就是全套大厂学习笔记面试指南吃透一半保你可以吊打面试官,只有自己真正强大了,有了核心竞争力,你才有拒绝offer权力,所以,奋斗吧!开发者们!千里之行,始于足下;种下一颗树最好的时间是十年前,其次,就是现在

上一篇下一篇

猜你喜欢

热点阅读