• Android事件分发流程总结


    Action_Down

    当按下一个控件,调用流程是Activity.dispatchTouchEvent -> ViewGroup.dispatchTouchEvent ,

    1、ViewGroup.dispatchTouchEvent返回true会消费掉当前的event,不会调用当前ViewGroup的onTouchEvent。

    2、ViewGroup.dispatchTouchEvent返回false会调用父控件的onTouchEvent方法。并且逐级往上层回溯onTouchEvent。

    3、如果ViewGroup要拦截event, ViewGroup.dispatchTouchEvent中调用的onInterceptTouchEvent返回true就会拦截当前事件,拦截到event,会在当前ViewGroup中调用onTouchEvent来处理event, onTouchEvent返回false则继续往父控件回溯; 返回true, 表示已消费当前事件,不再回溯。

    如果onInterceptTouchEvent返回false, 就会调用子控件的dispatchTouchEvent,一次类推,继续下去。

    4、View的disPatchTouchEvent如果返回true就会消费掉event,不会再传递给onTouchEvent。如果View要拦截Event,使得event消费在View的onTouchEvent中,由于View中没有onInterceptTouchEvent,怎么样传递给当前View的onTouchEvent呢? 答案是通过调用super.dispatchTouchEvent(在其内部调用到onTouchEvent)。

    5、一旦event进入到onTouchEvent中去处理,当前处理的onTouchEvent返回false会从下往上调用父控件的onTouchEvent, 知道控件的onTouchEvent返回true, 表示将该event消费掉。

    总结:dispatchTouchEvent返回true和ViewGroup的onInterceptTouchEvent返回true以及onTouchEvent返回true会消费掉事件。

    而真正处理事件的函数是可能是dispatchTouchEvent或者onTouchEvent,因为onInterceptTouchEvent返回true会在onTouchEvent中消费。

    dispatchTouchEvent方法能把事件分发到自己的onTouchEvent处理呢,return true和false 都不行,那么只能通过Interceptor把事件拦截下来给自己的onTouchEvent,所以ViewGroup dispatchTouchEvent方法的super默认实现就是去调用onInterceptTouchEvent

    Action_Move和Action_Up

    Action_Down会按照从上往下,寻找消费事件。

    如果消费事件是卡在dispatchTouchEvent那么Action_Move和Action_Up调用流程与Action_Down一致,都是同样流程被dispatchTouchEvent消费掉。

    如果消费事件是在onTouchEvent函数,Action_Move和Action_Up调用流程将会是在对应的dispatchTouchEvent->onTouchEvent消费,不会向下传递。而Action_Down会走完一个完整的流程。


    图片出自http://www.jianshu.com/p/e99b5e8bd67b

    源码:

    1、Activity中的dispatchTouchEvent:

    根据官方文档注释,当有任意一个按键、触屏或者轨迹球事件发生时,栈顶Activity的onUserInteraction会被触发。如果我们需要知道用户是不是正在和设备交互,可以在子类中重写这个方法,去获取通知(比如取消屏保这个场景)。(参考 http://allenfeng.com/2017/02/22/android-touch-event-transfer-mechanism/)

    2765行,getWindow().superDispatchTouchEvent(ev);getWindow是mWindow, mWindow实际上是PhoneWindow,所以

    调用DecorView中的superDispatchTouchEvent

    上图表明在DecorView中调用的是父类的dispatchTouchEvent(FrameLayout中没有重写dispatchTouchEvent,所以调用ViewGroup中的dispatchTouchEvent)

    现在回到Activity的dispatchTouchEvent看,如果getWindow().superDispatchTouchEvent(ev)返回false, 会调用Activity的onTouchEvent。

    2、继续分析在ViewGroup中开始调用dispatchTouchEvent

    具体分析见http://allenfeng.com/2017/02/22/android-touch-event-transfer-mechanism/

    重点说一处,在Action_Down的后续事件Action_Move或者Action_Up等事件,会传递至mFirstTouchTarget中保存的目标子View中,如果在上一节遍历过程中已经把本次事件传递给子View,alreadyDispatchedToNewTouchTarget的值会被设置为true,代码会判断alreadyDispatchedToNewTouchTarget的值,避免做重复分发。简单的说一个View没有消费Action_Down事件,后续的事件也不会传递近来。

    3、View的dispatchTouchEvent

    如果ViewGroup没有消费事件最终会调用到View的dispatchTouchEvent。

    View首先会调用onTouch,如果它返回true,那么onTouchEvent将得不到执行,事件传递终止,否则会即系传递,直到onTouchEvent返回true消费事件。

    onInterceptTouchEvent默认是不拦截的,即返回false;如果你需要拦截,只要return true就行了,这要该事件就不会往子View传递了,并且如果你在DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件。

    原因很简单,当onInterceptTouchEvent(ev) return true的时候,会把mMotionTarget 置为null ; 

    getParent().requestDisallowInterceptTouchEvent(true);  这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。

  • 相关阅读:
    (2)javascript的基本语法、数据结构、变量
    (1)认识javascript
    CSS 浅析position:relative/absolute定位方式
    jquery实现下拉框多选
    Vue.js not detected
    手机代理调试Charles Proxy和Fiddler
    render函数之jsx应用
    vue组件通信方式(多种方案)
    点击页面空白处地方,隐藏弹窗
    css圆角不圆和1px方案
  • 原文地址:https://www.cnblogs.com/SA226343/p/7359172.html
Copyright © 2020-2023  润新知