事件分发

    xiaoxiao2025-05-16  68

    注:以下存在伪代码示意事件分发流程,并非源码

    1.Activity分发事件:

    Activity分发事件的逻辑:

    public boolean dispatchTouchEvent(MotionEvent ev) { //直接将ev委托给Window处理,它的返回值意思是事件有没有被处理 if(getWindow().superDispatchTouchEvent(ev)) { //表示在该Activity中有View处理了该事件 return true; } //没有View愿意处理该事件,调用自身的onTouchEvent方法 return onTouchEvent(ev); }

    Window分发事件:

    public boolean superDispatchTouchEvent(MotionEvent ev) { //Window又直接把ev委托给decorView处理 return mDecor.superDispatchTouchEvent(ev); }

    2.ViewGroup分发事件:

    ViewGroup与事件分发相关的方法主要有三个:

    public boolean dispatchTouchEvent(MotionEvent ev) //用来控制事件的分发 public boolean onInterceptTouchEvent(MotionEvent ev) //用来判断是否拦截事件 public boolean onTouchEvent(MotionEvent ev) //ViewGroup处理事件

    还有三个重要的标记为来控制事件分发的整体流程:

    //是当前ViewGroup的一个子View,这个View消费了Down事件就会被赋值给这个变量 private TouchTargetmFirstTouchTarget //这个标记为通过requestDisallowInterceptTouchEvent方法设置 //该标记可以禁止ViewGroup拦截Move和Up事件,强制将这些事件分发给其子View,在滑动冲突的内部拦截中会用到 final static int FLAG_DISALLOW_INTERCEPT

    三个方法的调用关系:(伪代码)

    public boolean dispatchTouchEvent(MotionEvent ev) { boolean consumed = false; // 默认事件是没有被消费的 resetStatus() //(1) 如果是DOWN事件,需要重置标记位 if(shouldIntercepted(ev)){ //(2) 如果需要拦截,就不下发事件,直接调用onTouchEvent自己处理该事件 consumed = onTouchEvent(ev) }else { consumed = dispatchToChild(ev); //(3) 如果不需要拦截,下发事件 } return consumed; }

    (1)清除状态

    public void resetStatus(MotionEvent ev) { //DOWN事件说明重新开启了一个事件序列,重置标记位 if(ev.getAction == MotionEvent.ACTION_DOWN) { //清除TouchTargetmFirstTouchTarget cancelAndClearTouchTargets(ev); //清除FLAG_DISALLOW_INTERCEPT标记 //所以requestDisallowInterceptTouchEvent不能影响Down事件拦截判断 resetTouchState(); } }

    (2)判断是否需要拦截事件

    public boolean shouldIntercepted(MotionEvent ev) { final boolean intercepted; if(action == MotionEvent.ACITON_DOWN || mFristTouchTarget != null) { boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if!disallowIntercept){ //disallowIntercept在检查到是DOWN事件时已经被重置为false了,所以DOWN事件一定去会检查是否拦截 intercepted = onInterceptTouchEvent(ev); } else { intercepted = false; } } else { //不是DOWN事件,也没有子View消费过Down事件,就直接拦截这个事件自己处理 //实际上如果一个DOWN事件被当前ViewGroup拦截了,mFristTouchTarget一定为null,也会直接走这个分支 intercepted = true; } }

    总结: 1.ViewGroup的onInterceptTouchEvent方法不是每个事件到来都会调用: 2.即使onInterceptTouchEvent不拦截事件,最后事件也可能被拦截,因为子View都不消费这个事件

    (3)分发事件给子View: 遍历子View获取点击到的并触发其dispatchTouchEvent方法

    public boolean dispatchToChild(MotionEvent ev) { for(View child: children) { //遍历所有子View if(isTouched(child)) { //如果触碰到了这个View boolean consumed = child.dispatchTouchEvent(ev); //就把这个事件分发给这个View处理 if(consumed) mFristTouchTarget = child; //这个子View消费了事件,就把mFristTouchTarget 赋值 return consumed; } } return false; }

    3.View分发事件:

    public boolean dispatchTouchEvent(MotionEvent ev) { if(mOnTouchListener != null) { //设置了mOnTouchListener的话就可能屏蔽掉onTouchEvent方法 consumed = mOnTouchListener.onTouch(ev); if(consumed) { return true; }else{ //是否屏蔽onTouchEvent取决于mOnTouchListener.onTouch的返回值 onTouchEvent(ev); } } //没有设置mOnTouchListener的话就直接调用ViewGroup自己的onTouchEvent方法处理事件 return onTouchEvent(ev); }

    总结: 事件处理优先级:onTouchListener(手动设定) > onTouchEvent(系统自带) > onClickListener(手动设定)

    参考:Android开发艺术探索

    最新回复(0)