Android事件分发机制

  • 当DecorView接收到事件后
// DecorView.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        // 第三步
        // 事件会从Activity中分发到这里
        return mDecor.superDispatchTouchEvent(event);
    }
    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            // 第一步
            // cb是什么?
            // 看Activity.attach().
            // cb接口由Activity对象实现
            final Callback cb = getCallback();
            // 那么最终调用到cb.dispatchTouchEvent(ev),逻辑去到Activity中.
            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev): super.dispatchTouchEvent(ev);
        }
        public boolean superDispatchTouchEvent(MotionEvent event) {
            // 第四步
            // 事件最终被分发到此处,DecorView的父类其实就是ViewGroup对象,那么事件现在被分发到ViewGroup中了.
            return super.dispatchTouchEvent(event);
        }
    }
}
// Activity.java
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        ...
        // 将Activity对象设置给PhoneWindow的mCallback变量中
        mWindow.setCallback(this);
        ...
    }
    // 当Activity接收到DecorView事件分发
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        // 第二步
        // getWindow().superDispatchTouchEvent(ev),事件被分发到Window中.
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
}
  • 当ViewGroup接收到事件后
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        ...
        boolean handled = false;
        //onFilterTouchEventForSecurity():根据隐私策略而来决定是否过滤本次触摸事件,
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            // 获取事件类型
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            // 开始了新的事件序列,将之前的状态全部放弃
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
            final boolean intercepted;
            // 发生ACTION_DOWN事件或者已经发生过ACTION_DOWN,才进入此区域.
            // 只有发生过ACTION_DOWN事件,则mFirstTouchTarget != null.
            if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
                // 可通过调用requestDisallowInterceptTouchEvent,不让父View拦截事件
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                // 判断是否允许调用拦截器
                if (!disallowIntercept) {
                    // ViewGroup中,onInterceptTouchEvent()默认返回false(不拦截).
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action);
                } else {
                    intercepted = false;
                }
            } else {
                // 如果是ACTION_DOWN以外的事件,且ACTION_DOWN事件没有找到目标子View接收消耗,那么ViewGroup就拦截这些事件.
                intercepted = true;
            }
            ...
            // 当前View将要从父控件中剥离,resetCancelNextUpFlag()将返回true.
            // 当前事件类型为ACTION_CANCEL时,canceled变量也将为true.
            final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;
            TouchTarget newTouchTarget = null;// 消耗了ACTION_DOWN事件子View将存到newTouchTarget中.
            boolean alreadyDispatchedToNewTouchTarget = false;// 是否准备好了新的接收事件子View
            // 如果当前事件不是ACTION_CANCEL事件,并且ViewGroup不拦截该事件
            if (!canceled && !intercepted) {
                ...
                // 如果该事件是ACTION_DOWN,ACTION_POINTER_DOWN,ACTION_HOVER_MOVE,那么就开始寻找可以接收该DOWM类事件的子View对象.
                if (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    ...
                    final int childrenCount = mChildrenCount;
                    // 如果还没有找到目标子View,切子View个数不为0
                    if (newTouchTarget == null && childrenCount != 0) {
                        ...
                        // 遍历所有子View,寻找能接收Down事件的子View.
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            ...
                            // dispatchTransformedTouchEvent():将Down事件分发到子View,如果该子View消耗了该Down事件的话.
                            // 那么该子View就是目标子View.
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                ... 
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                // addTouchTarget():将目标子View装进newTouchTarget对象中.
                                // 如果找到了能接收Down事件的子View,此时newTouchTarget == mFirstTouchTarget.这个是由addTouchTarget()做的.
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                // 这个标识表示已经准备好了目标子View.
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                            ...
                        }
                        ...
                    }
                   ...
                }
            }
            if (mFirstTouchTarget == null) {
                // 假如没有找到目标子View接收Down事件,那么就会走到这里.
                // 参数child如果为null,那么该事件就会交由当前父控件处理.
                // 稍后看该方法
                handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
            } else {
                // 假如找到了目标子View接收Down事件,走这里.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        // 1. alreadyDispatchedToNewTouchTarget==true:目标子View已经准备完毕,
                        //    这里需要说明一点是,alreadyDispatchedToNewTouchTarget是一个成员方法变量,每次走到dispatchTouchEvent()中,alreadyDispatchedToNewTouchTarget最开始都为false.
                        //    只有事件为Down,且找到目标子View时,alreadyDispatchedToNewTouchTarget才等于true.
                        // 2. newTouchTarget == mFirstTouchTarget 这在目标子View成功消耗Down事件时候所设置的.看上面
                        // handled此时为true,表示的就是Down事件被ViewGroup中某个子View消耗掉了.
                        handled = true;
                    } else {
                        // 这里就表示为Down以外的事件分发流程了.
                        // intercepted表示ViewGroup是否拦截该次事件.
                        // 拦截时intercepted=true则cancelChild=true,不拦截时intercepted=false则cancelChild=false.
                        final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                        // 这里是将事件分发到目标子View中,dispatchTransformedTouchEvent()下面分析.
                        if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
                            // 如果目标子View消耗了该事件,ViewGroup.dispatchTouchEvent()就返回true.
                            handled = true;
                        }
                        // 如果ViewGruop拦截事件
                        if (cancelChild) {
                            if (predecessor == null) {
                                // 这句话意思是mFirstTouchTarget将被他的上一个TouchTarget取代.TouchTarget是一个链表结构
                                // mFirstTouchTarget对象中的当前目标子View对象,今后将不会在接收到当前的事件序列中的任何事件了.
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            ...
                            continue;
                        }
                        ...
                    }
                }
                ...
            }
            // 如果是ACTION_CANCEL一类事件
            if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                // 清除TouchTarget链表
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                ...
            }
        }
        ...
        return handled;
    }
    // ViewGroup处理事件的地方
    // 从上面的dispatchTouchEvent()中可知
    // 1. 当接收到Down事件,如果正好有子View消耗该Down事件,dispatchTransformedTouchEvent()会被调用,此时cancel= false.
    // 2. 当没有找到目标子View消耗Down事件,接收除Down以外的事件会触发该方法,此时child =null
    // 3. 当找到目标子View,接收除了Down以外的事件会触发该方法,此时如果ViewGroup拦截事件,那么cancel=true.
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {
        final boolean handled;
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            // 接收到ACTION_CANCEL事件或者事件被ViewGroup拦截,就会走这里.
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                // 如果目标子View为null,将ACTION_CANCEL事件交由ViewGroup
                handled = super.dispatchTouchEvent(event);
            } else {
                // 如果目标子View存在,将ACTION_CANCEL事件交由目标子控件.
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            // 返回处理结果
            return handled;
        }
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
        ...
        if (child == null) {
            // 不存在子视图时,ViewGroup调用View.dispatchTouchEvent分发事件,再调用ViewGroup.onTouchEvent来处理事件
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            ...
            // 将触摸事件分发给子ViewGroup或View;
            handled = child.dispatchTouchEvent(transformedEvent);
        }
        ...
        return handled;
    }
}
  • View.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent event) {
    ...
    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        //在Down事件之前,如果存在滚动操作则停止。不存在则不进行操作
        stopNestedScroll();
    }
    ...
    ListenerInfo li = mListenerInfo;
    //从这里可以看出,条件允许onTouch方法要早于onTouchEvent调用
    if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {
        result = true;
    }
    // 如果OnTouch()没有消费Touch事件则调用OnTouchEvent()
    if (!result && onTouchEvent(event)) {
        result = true;
    }
	...
    return result;
}
  • View.onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
	...
    //这里可以看到,只有view能被点击或者被长按点击,该View才可以消费事件.    
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    ...
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
				...
                //执行点击操作   
                performClickInternal();
 				...
                break;
            case MotionEvent.ACTION_DOWN:
				...
                break;
            case MotionEvent.ACTION_CANCEL:
               	...
                break;
            case MotionEvent.ACTION_MOVE:
                ...
                break;
        }
        return true;
    }
    return false;
}
  • View点击回调
private boolean performClickInternal() {
    notifyAutofillManagerOnClick();
    return performClick();
}
public boolean performClick() {
	...
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        //执行onClickListener回调
        li.mOnClickListener.onClick(this);
        result = true;
    } else {
        result = false;
    }
	...
    return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值