• Android触摸事件传递机制,这一篇就够了


    整个触摸事件牵涉到的是,Activity,View,ViewGroup三者的传递机制。
    这个触摸事件就是从外层往内层一层层的传递。
    整个传递机制,分为3个步骤:分发,拦截,和消费。

    1. 触摸事件的类型

    事件类型是MotionEvent类:看下最新的sdk29的源码,一堆的Action,我们常用的其实就3个,ACTION_DOWN,ACTION_MOVE,ACTION_UP。
    Down和Up事件,是触摸过程中一定会发生的事件。Move事件看具体的情况。

    2.事件传递的顺序

    从外层往内层一层层的传递,外层是那个,内层是那个?
    我们从android系统的角度看。事件首先传给的肯定是Activity,因为Activity的UI布局,事物逻辑,作为底层来说,不知道,也并不关心。
    所以只要把事件送到Activity层,就可以。
    同样的道理,事件先送到ViewGroup,然后在送到View。这样从外层到内层的过程。
    其实每一层都会对触摸事件有响应,这个事件是有具体的业务逻辑决定的,所以我们需要一些操作来控制这个事件的过程。
    比如点击事件,假设点击了某个按钮,这样它的父ViewGroup要不要处理,页面要不要处理,从表面来看他们都被点击了,从实际人们的经验来看,
    就是View被点击了,不需要ViewGroup和Activtiy后面2个事件触发。
    Activity->ViewGroup->ViewGroup...->View 整个事件流程传递就是这样。
    View->ViewGroup...->ViewGroup->Activity 这个就是事件的消费过程。

    3.事件传递的3个阶段

    所以android把触摸事件传递分为3个步骤:分发,拦截,和消费。

    3.1 分发

    分发:事件分发对应着dispatchTouchEvent方法,在Android中,所有的触摸事件都是这个方法来分发的。

    public boolean dispatchTouchEvent(MotionEvent ev)
    

    3.1.1 Activity分发

    分发是触摸事件首先到达。

    • 返回true,表示事件被消耗掉。不需要分发下去。消费了事件,后续的事件会继续传递到这里。
    • 返回false,表示事件被废弃掉。因为dispatchTouchEvent是总入口,所以就算是false,后续事件还会出现。
    • 返回值是super.dispatchTouchEvent,事件继续传递下去,这个是整个事件传递的中枢,从源码看,一切从这来开始,也从这里结束。
      如果view hierarchy都不处理这个touch事件,就由Activity的onTouch事件来处理。可见上面第二节的结论是正确的。最先接收,最后处理。
    /**
         * Called to process touch screen events.  You can override this to
         * intercept all touch screen events before they are dispatched to the
         * window.  Be sure to call this implementation for touch screen events
         * that should be handled normally.
         *
         * @param ev The touch screen event.
         *
         * @return boolean Return true if this event was consumed.
         */
        public boolean dispatchTouchEvent(MotionEvent ev) {
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                onUserInteraction();
            }
            if (getWindow().superDispatchTouchEvent(ev)) {
                return true;
            }
            return onTouchEvent(ev);
        }
    

    3.1.2VIewGroup分发

    • 返回true,表示事件被消耗掉。不需要分发下去。消费了事件,后续的事件会继续传递到这里。
    • 返回false,表示事件被废弃掉。后续的其他事件不会被传递到这里。
    • 返回值是super.dispatchTouchEvent,事件继续传递下去,这个是整个事件传递的中枢,从源码看,ViewGroup的分发流程分以下几步
      有拦截,拦截了touch事件,就走ViewGroup自己的onTouchEvent。
      没有拦截,就找到触摸点的位置,上面是否有View,有就调view.dispatchTouchEvent,如果没有,就直接交给ViewGroup处理。
      这里还有一点,如果action_down已经处理了,后面action_up 和 action_move都使用相同的处理方式。

    3.1.3 View的分发

    View的分发,也许会觉得奇怪,分发给谁?
    从源码看,2个途径,onTouch listener,或者onTouchEvent。前者优先级必后者高。

    • 返回true,表示事件被消耗掉。不需要分发下去。消费了事件,后续的事件会继续传递到这里。并且这里会认为事件被View消费掉了,所以上层的VIewGroup不会收到要处理onTouchEvent的事件。
    • 返回false,表示事件被废弃掉。后续的其他事件不会被传递到这里。而且可以理解为,我这个View不想处理这个事件,所以ViewGroup会触发onTouchEvent事件。
    • 返回值是super.dispatchTouchEvent
                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;
                }
    

    onTouch是否处理,return true,就表面被第三方处理了,不需要onTouchEvent来处理。不然就是onTouchEvent处理。

    3.2 拦截

    事件拦截对应onInterceptTouchEvent。事件是从外向内传递的,当事件在分发的过程中,要不要拦截,

    public boolean onInterceptTouchEvent(MotionEvent ev)
    

    有些情况下需要子View来响应,有些情况需要当前ViewGroup来处理。这个方法Activity没有,只有ViewGroup才有。
    这个简单来说就是,是否需要分发事件给到子View,true就是不分发。

    3.3 消费

    其实dispatchTouchEvent也有消费的概念在里面。
    如果说分发的流程,是从外层到内层,
    消费的流程正好相反,从内层到外层。

    public boolean onTouchEvent(MotionEvent event) 
    
    • 返回true,消费事件,不在向外层传递
      -返回false,不消费事件,向外层传递
      -返回spuer.onTouchEvent,这里会判断是否是click事件。只有这个写法才会触发click事件。

    4. 流程图

    5.总结

    流程很复杂,每个部分返回不同的true/fase/super都会产生不同的流程结果。
    事件分发流程:
    从外层到内层分发,
    从内层到外层消费。

    dispatchTouchEvent 和onTouchEvent,返回true,代表由自己处理这个事情。阻断后面的流程。
    dispatchTouchEvent 和onTouchEvent,返回false,表示不由自己来处理这个事件,流程交由上一层级来处理。
    dispatchTouchEvent 和onTouchEvent,返回spuer,就是上面的流程。

    更多内容:demanmath
    公共号:

  • 相关阅读:
    Java中的集合类-详解
    wargames-Leviathan
    词霸阿涛的英语学习经历
    《小王子》阅读笔记
    linux的mysql密码忘了怎么办
    redis事务实现
    缓存穿透、缓存击穿、缓存雪崩
    单线程redis为什么快?
    redis和么memcached的区别
    如何解决缓存污染
  • 原文地址:https://www.cnblogs.com/deman/p/12294854.html
Copyright © 2020-2023  润新知