Android事件分发和View绘制流程分析(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/M075097/article/details/79027195

Android事件分发和View绘制流程分析(二)

在上一篇文章中我们从Activity的初始化开始最终走到了ViewRootImpl的setView()方法中,下面我们就其中的事件分发继续查阅代码如下<下面涉及到的Android源码均为API23的源码>

一、对于事件分发

1.1 硬件驱动层的事件经底层拿取之后最终首先会到达我们之前提到过的ViewRootImpl中初始化的WindowInputEventReceiver中,WindowInputEventReceiver是ViewRootImpl的一个内部类,我们先看一下WindowInputEventReceiver的实现:

1.1.1 在接收到事件后,调用enqueueInputEvent(event, this, 0, true),最后一个参数为processImmediately 是否立即处理,如果为true则会直接开始处理,否则会顺序压入主线程的MessageQueue中
@ViewRootImpl$WindowInputEventReceiver
final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);
    }

    @Override
    public void onInputEvent(InputEvent event) {
        enqueueInputEvent(event, this, 0, true);//----1.1.1接收到事件开始处理事件,最后一个参数为processImmediately 是否立即处理,如果为true则会直接开始处理,否则会顺序压入MessageQueue中
    }

    @Override
    public void onBatchedInputEventPending() {
        if (mUnbufferedInputDispatch) {
            super.onBatchedInputEventPending();
        } else {
            scheduleConsumeBatchedInput();
        }
    }

    @Override
    public void dispose() {
        unscheduleConsumeBatchedInput();
        super.dispose();
    }
}

1.2 在WindowInputEventReceiver中接收到事件之后调用的enqueueInputEvent()实现如下,其中的参数processImmediately:是否立即处理,我们下面只看一下立即处理时的调用逻辑,是从doProcessInputEvents()开始的。对于压入MessageQueue的事件处理,最终依旧会通过ViewRootImpl中的ViewRootHandler再次调用到 doProcessInputEvents();

@ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    adjustInputEventForCompatibility(event);
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

    // Always enqueue the input event in order, regardless of its time stamp.
    // We do this because the application or the IME may inject key events
    // in response to touch events and we want to ensure that the injected keys
    // are processed in the order they were received and we cannot trust that
    // the time stamp of injected events are monotonic.
    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;
    Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
            mPendingInputEventCount);

    if (processImmediately) {
        doProcessInputEvents();//立即处理接收到的事件
    } else {
        scheduleProcessInputEvents();//
    }
}

1.3 然后通过以下调用:doProcessInputEvents()–>deliverInputEvent(q),具体代码如下:

1.3.1 根据是否直接使用兜底的事件拦截处理器,以及是否需要跳过输入法的拦截处理器选择相关的InputStage链的头
1.3.2 最终调用会走到:stage.deliver(q),此处的stage即为1.3.1中初始化的InputStage,为了可以清晰的理清这个处理链的关系,在1.4中我们再次贴出其初始化时的相关代码
    @ViewRootImpl.java
    void doProcessInputEvents() {
     // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            deliverInputEvent(q);//----调用deliverInputEvent(q)
        }

        // We are done processing all input events that we can process right now
        // so we can clear the pending flag immediately.
        if (mProcessInputEventsScheduled) {
            mProcessInputEventsScheduled = false;
            mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
        }
    }

    //继续调用到此处
    private void deliverInputEvent(QueuedInputEvent q) {
    Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
            q.mEvent.getSequenceNumber());
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
    }

    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;//在ViewRootImpl的setView()中初始化的为SyntheticInputStage
    } else {
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; //mFirstPostImeInputStage为在ViewRootImpl的中初始化的 EarlyPostImeInputStage/mFirstInputStage为:NativePreImeInputStage
    }

    if (stage != null) {
        stage.deliver(q);//调用InputStage的deliver()
    } else {
        finishInputEvent(q);
    }
}

1.4 我们重新贴出在ViewRootImpl中初始化的事件拦截处理器的代码,我们先看在ViewRootImpl的setView方法中最后构建的7个InputStage代码重新粘贴如下:

@ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
        if (mView == null) {
            mView = view;//------此处即为DecorView,事件传递时,到达ViewRootImpl之后调用的即是DecorView的disPatchTouchEvent()
            ...
             requestLayout();//===******===请求布局开始,此处为View绘制的起始点
            ...
            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,Looper.myLooper());//===******===设置底层事件接收Receiver,此处是事件分发进入用户Application层的接入起始点
            }
            ...
            //------1.6.4、外部事件(点击,按键等)地处理Handler的初始化,以下构造的事件处理Handler是以单向链表的方式组织,处理是以职责链的方式进行处理
              // Set up the input pipeline.
            CharSequence counterSuffix = attrs.getTitle();
            mSyntheticInputStage = new SyntheticInputStage();//----1.是一个兜底消息处理器,当前面的处理器都没有消费掉此次事件时最终会使用该处理器进行处理
            InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);//----####---2.该消息处理器中会调用mView【DecorView】的dispatchTouchEvent()或Keyevent进行转发,进而进入到Activity,再回转到mDecorView
            InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,//----3.native的一个处理器 不太清楚是干什么的
                    "aq:native-post-ime:" + counterSuffix);
            InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);//----4.输入法相关的拦截处理器,处理完后首先调用到的
            InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                    "aq:ime:" + counterSuffix);//-----5【和输入法相关的处理】相关的
            InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);//-----6.在在输入法处理之前调用,该处理中会回调所有View的dispatchKeyEventPreIme(event),进而调用onKeyPreIme()方法
            InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                    "aq:native-pre-ime:" + counterSuffix);----7 native相关的在 输入法拦截处理器之前调用

            mFirstInputStage = nativePreImeStage;//---此处初始化后面事件处理时需要调用的mFirstInputStage
            mFirstPostImeInputStage = earlyPostImeStage;//---此处初始化后面事件处理时需要调用的mFirstPostImeInputStage
            mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
        }
    }
}
1.4.1 该7个事件处理Stage都是继承自抽象类InputStage。该抽象类中有一个mNext字段,从而可以构成一个链表结构。用来顺序处理接收到的事件

InputStage是一个抽象类其实现可以组成一个单向链表结构,实现如下

@ViewRootImpl$InputStage
abstract class InputStage {
    private final InputStage mNext;//----记录链表的下一个节点的InputStage

    protected static final int FORWARD = 0;//当前InputStage已经处理完毕并调用下一个InputStage进行处理
    protected static final int FINISH_HANDLED = 1;//当前InputStage处理完毕并结束链式调用
    protected static final int FINISH_NOT_HANDLED = 2;//当前InputStage没有处理,结束链式调用

    /**
     * Creates an input stage.
     * @param next The next stage to which events should be forwarded.
     */
    public InputStage(InputStage next) {
        mNext = next;//----在构造时传入链表的下一个节点并保存,当需要forward时调用该next节点进行处理,直到链表结束或者事件得到应有处理
    }

    /**
     * Delivers an event to be processed.
     */
    public final void deliver(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);//----直接调用下一个拦截处理器
        } else if (shouldDropInputEvent(q)) {//是否遗弃对事件的处理
            finish(q, false);
        } else {
            apply(q, onProcess(q));//---使用当前InputStage进行处理,**并根据onProcess()返回决定是否调用下一个InputStage
        }
    }

    /**
     * Marks the the input event as finished then forwards it to the next stage.
     */
    protected void finish(QueuedInputEvent q, boolean handled) {//---处理完成更改相关的标记位并调用forward(),进入到下一个inputStage处理
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
        if (handled) {
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
        }
        forward(q);
    }

    /**
     * Forwards the event to the next stage.
     */
    protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
    }

    /**
     * Applies a result code from {@link #onProcess} to the specified event.
     */
    protected void apply(QueuedInputEvent q, int result) {
        if (result == FORWARD) {
            forward(q);
        } else if (result == FINISH_HANDLED) {
            finish(q, true);
        } else if (result == FINISH_NOT_HANDLED) {
            finish(q, false);
        } else {
            throw new IllegalArgumentException("Invalid result: " + result);
        }
    }

    /**
     * Called when an event is ready to be processed.
     * @return A result code indicating how the event was handled.
     */
    protected int onProcess(QueuedInputEvent q) {
        return FORWARD;
    }

    /**
     * Called when an event is being delivered to the next stage.
     */
    protected void onDeliverToNext(QueuedInputEvent q) {
        if (DEBUG_INPUT_STAGES) {
            Log.v(TAG, "Done with " + getClass().getSimpleName() + ". " + q);
        }
        if (mNext != null) {
            mNext.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

    protected boolean shouldDropInputEvent(QueuedInputEvent q) {
       ...//省略代码 具体可直接参考 此处判定当前事件是否需要丢弃
    }

    void dump(String prefix, PrintWriter writer) {
        if (mNext != null) {
            mNext.dump(prefix, writer);
        }
    }

    private boolean isBack(InputEvent event) {
        if (event instanceof KeyEvent) {
            return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK;
        } else {
            return false;
        }
    }
}

1.5 在1.4中看到的Inputstage中,有一个ViewPostImeInputStage,该拦截处理器中会把时间分发到具体的View树上,在ViewPostImeInputStage的处理onProcess()中会根据事件类型最终调用到mView.dispatchKeyEventPreIme(event),而mView即为DecorView(是在ViewRootImpl的setView()中设置的),代码如下:

1.5.1 如果是KeyEvent则调用processKeyEvent(q) 处理keyEvent事件
1.5.2 如果是触摸事件则调用 processPointerEvent(q) 处理触摸事件
1.5.3 处理KeyEvent事件会调用到mView.dispatchKeyEvent(event),在1.6中我们具体看又是如何处理的
1.5.4 处理触摸事件会调用到mView.dispatchPointerEvent(event),在1.6中我们在具体看是如何处理的
@ViewRootImpl$ViewPostImeInputStage
final class ViewPostImeInputStage extends InputStage {
    public ViewPostImeInputStage(InputStage next) {
        super(next);
    }

    @Override
    protected int onProcess(QueuedInputEvent q) {
        if (q.mEvent instanceof KeyEvent) {//如果是KeyEvent
            return processKeyEvent(q);//----------1.5.1、处理keyEvent事件------------
        } else {//非keyEvent事件
            // If delivering a new non-key event, make sure the window is
            // now allowed to start updating.
            handleDispatchWindowAnimationStopped();
            final int source = q.mEvent.getSource();
            if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {//触摸事件
                return processPointerEvent(q);//----------1.5.2、处理TouchEvent事件------
            } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {//轨迹球
                return processTrackballEvent(q);
            } else {//---其他类型的事件
                return processGenericMotionEvent(q);
            }
        }
    }

    @Override
    protected void onDeliverToNext(QueuedInputEvent q) {
        if (mUnbufferedInputDispatch
                && q.mEvent instanceof MotionEvent
                && ((MotionEvent)q.mEvent).isTouchEvent()
                && isTerminalInputEvent(q.mEvent)) {
            mUnbufferedInputDispatch = false;
            scheduleConsumeBatchedInput();
        }
        super.onDeliverToNext(q);
    }

    //处理KeyEvent事件
    private int processKeyEvent(QueuedInputEvent q) {
        final KeyEvent event = (KeyEvent)q.mEvent;

        if (event.getAction() != KeyEvent.ACTION_UP) {
            // If delivering a new key event, make sure the window is
            // now allowed to start updating.
            handleDispatchWindowAnimationStopped();
        }

        // Deliver the key to the view hierarchy.
        if (mView.dispatchKeyEvent(event)) {//--------1.5.3此处开始传递KeyEvent到View树,此处的mView即为DecorView-------------
            return FINISH_HANDLED;//如果事件消费掉则至此结束不再调用链表后面的InputStage
        }

        if (shouldDropInputEvent(q)) {
            return FINISH_NOT_HANDLED;
        }

        ....//省略代码

        return FORWARD;
    }

    //触摸事件的处理
    private int processPointerEvent(QueuedInputEvent q) {
        final MotionEvent event = (MotionEvent)q.mEvent;

        mAttachInfo.mUnbufferedDispatchRequested = false;
        boolean handled = mView.dispatchPointerEvent(event);//---------1.5.4传递触摸事件到DecorView,进而到整个View树上,此处的mView即为DecorView---
        if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
            mUnbufferedInputDispatch = true;
            if (mConsumeBatchedInputScheduled) {
                scheduleConsumeBatchedInputImmediately();
            }
        }
        return handled ? FINISH_HANDLED : FORWARD;//如果已经成功消费该事件则停止链式调用
    }

    //轨迹球事件处理
    private int processTrackballEvent(QueuedInputEvent q) {
        ...
        return FORWARD;
    }
    //其他事件处理
    private int processGenericMotionEvent(QueuedInputEvent q) {
        ...
        return FORWARD;
    }
}

1.6 现在我们再来回顾以上整个事件传递流程:事件是在ViewRootImpl的setView()中初始化的WindowInputEventReceiver中接收的,并最终传递到DecorView并根据事件类型调用了DecorView的dispatchKeyEvent(KeyEvent event)和DecorView的dispatchTouchEvent(MotionEvent ev),下面我们看DecorView中的代码实现:

@PhoneWindoe.DecorView
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

          @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
       ...
        if (!isDestroyed()) {
            final Callback cb = getCallback();
            final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)//-----1.6.1此处先调用Activity的dispatchKeyEvent()--------
                    : super.dispatchKeyEvent(event);
            if (handled) {
                return true;
            }
        }

        return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
                : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
    }

    @Override
    public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
            ...

        // Shortcut not handled by the panel.  Dispatch to the view hierarchy.
        final Callback cb = getCallback();
        handled = cb != null && !isDestroyed() && mFeatureId < 0
                ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
        if (handled) {
            return true;
        }
        ...
        return false;
    }

      @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {/-----/该处逻辑在ViewRootImpl中调用mView.dispatchTouchEvent()------
        final Callback cb = getCallback();//----此处拿取的Callback是在PhoneWindow的初始化之后设置进去的Activity,Activity继承自Window.CallBac,所以此处会先调用Activity的dispatchTouchEvent()------
        return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                : super.dispatchTouchEvent(ev);
    }

}
1.6.1 此时时事件都传递到了:Callback中进行处理,而该Callback正是Activity,我们看一下PhoneWindow中的getCallback()实现:
@PhoneWindow
 public final Callback getCallback() {
    return mCallback;//----此处的Callback是在ActivityThread中的performLunchActivity中的Activity.attch()中初始化完PhoneWindow就设置的一个Callback,其实际就是一个Activity------
}
1.6.2 我们重新整理mCallBack的初始化位置:在ActivityThread中的performLunchActivity中的Activity.attch()方法内,会初始化PhoneWindow,并调用PhoneWindow的setCallback(this)方法,把当前Activity设定为一个CallBack,而Activity也确实实现了PhoneWindow.CallBack接口,下面重新贴出Activity的attatch()相关代码:
@Activity.java
 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) {
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

    mWindow = new PhoneWindow(this);//初始化PhoneWindow
    mWindow.setCallback(this);//设置PhoneWindow内的CallBack为this即为Activity本身
    mWindow.setOnWindowDismissedCallback(this);
    ...

    //设置PhoneWindow中的WindowManager
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();//Activity中也保存一份该WindowManager的引用
    mCurrentConfig = config;
}

因此此时事件处理的具体逻辑进入到了Activity中的dispatchTouchEvent(ev)和dispatchTouchEvent(MotionEvent ev)中

1.7 从上面可知事件经层层处理最终进入到Activity中的dispatchTouchEvent(MotionEvent ev)和dispatchKeyEvent(event)实现如下

1.7.1、在以下代码中我们可以看到事件传递进来会首先进入到Activity的dispatchTouchEvent()当所有Activity下的View不消费该次事件时会回调到Activity的 onTouchEvent(ev)
1.7.2、同样dispatchKeyEvent()中在所有子View不消费该事件时会调用event.dispatch(this, decor != null? decor.getKeyDispatcherState() : null, this); 最终调用到Activity的onKeyDown()onKeyUp()等方法
 @Activity.java
 public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {//----此处又调用PhoneWindow的superDispatchTouchEvent(ev)
        return true;
    }
    return onTouchEvent(ev);
}

public boolean dispatchKeyEvent(KeyEvent event) {
    onUserInteraction();

    // Let action bars open menus in response to the menu key prioritized over
    // the window handling it
    if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
            mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
        return true;
    }

    Window win = getWindow();
    if (win.superDispatchKeyEvent(event)) {//--此处又调用PhoneWindow的superDispatchKeyEvent(event)
        return true;
    }
    View decor = mDecor;
    if (decor == null) decor = win.getDecorView();
    return event.dispatch(this, decor != null
            ? decor.getKeyDispatcherState() : null, this);
}   

1.8 由1.7中的代码可知:PhoneWindow的superDispatchTouchEvent()和superDispatchKeyEvent(event)中都直接又重新回到DecorView中调用了DecorView中的相关分发方法,实现如下:

@PhoneWindow.java
  @Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);//调用DecorView的superDispatchTouchEvent(event)
}
   @Override
public boolean superDispatchKeyEvent(KeyEvent event) {
    return mDecor.superDispatchKeyEvent(event);// 调用DecorView的superDispatchKeyEvent(event)
}

1.9 DecorView的superDispatchTouchEvent(event)和superDispatchKeyEvent(event)相关实现如下,其最终会调用到View的dispatchKeyEvent(event)和dispatchTouchEvent(event):

@PhoneWindow.DecorView
 public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

 public boolean superDispatchKeyEvent(KeyEvent event) {
        // Give priority to closing action modes if applicable.
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            final int action = event.getAction();
            // Back cancels action modes first.
            if (mPrimaryActionMode != null) {
                if (action == KeyEvent.ACTION_UP) {
                    mPrimaryActionMode.finish();
                }
                return true;
            }
        }

        return super.dispatchKeyEvent(event);
    }

1.10 由于DecorView继承自FrameLayout,是一个ViewGroup,至此开始调用ViewGroup的【事件分发】逻辑, 也就进入了我们常见的ViewGroup的事件分发分析,逻辑及代码如下:

我们以dispatchTouchEvent(event)为例简单说明一下ViewGroup的大致事件分发流程。【至于View的事件处理机制,已经有很多的参考,并且ViewGroup当没有子View消费事件时,其自己的处理就是一个View的处理逻辑(毕竟ViewGroup也是继承值View的)这里就不再赘述】

1.10.1 对于ViewGroup在dispatchKeyevent()中,当检测到是ACTION_DOWN的时候,说明是一个新的Touch序列到来,则清除之前所有状态值,以及之前保存的所有缓存的数据
1.10.2 如果是ACTION_DOWN(即当前为新的Touch事件序列到来)或者当前的mFirstTouchTarget不为null(即已经有该ViewGroup的子View该事件序列进行了消费),intercepted值(用来标识事件是否由该ViewGroup所代表的View进行拦截,如果拦截则其子View不会收到后续的其他事件)根据以下逻辑赋值:
  • 1.10.2.1.在disallowIntercept为false时(TODO:该值默认为false,我们常见的修改该值得方法是:View的getParent().requestDisallowInterceptTouchEvent()),即没有强制设置标志位,使得该ViewGroup不允许去拦截该事件时,则根据方法onInterceptTouchEvent(ev)返回的结果赋值;
  • 1.10.2.2.否则,在disallowIntercept为true时,即强制设置了标志位,使得不允许该ViewGroup去拦截事件,则intercepted 为false;
  • 当该事件是一个序列中的非初始事件(非ActionDown),且之前的事件并没有被任何子View消费,则此时intercept 置为 true;如果为ActionDown事件或者有子View处理过之前的事件,但是当前ViewGroup已经被设置为了不允许阻拦事件则intercept为false,如果没有设置不允许阻拦事件则根据onInterceptTouchEvent(ev)的值进行赋值 TODO delete
1.10.3、在非1.10.2的条件下(即当Event的Action不为DOWN且mFirstTouchTarget==null时候),对应场景为:然后当该事件是一个序列中的非初始事件(非ActionDown),且之前的事件并没有被任何子View消费,则此时intercept 置为 true;
  • 1、当intercept为true或者当前事件为Cancle时,跳过 1.10.4和1.10.5 直接到达1.10.6处执行
  • 2、当intercept为false时且当前事件为不是Cancel时,如果当前事件的Action不为ACTIONDOWN时,也跳过 1.10.4和1.10.5 直接到达1.10.6处执行,否则即当前事件是ActionDown,且当前ViewGroup并不拦截、处理该事件,则会执行遍历子View把该事件依次分发到子View上,直到找到目标View
  • 3、当intercept为false且当前事件的Action为ACTION_DOWN时则执行 顺序执行1.10.4和1.10.5
1.10.4 在遍历子View中 找出当前事件可及且当前点击事件位置所在的View,把该事件分发出去,分发时要更改Action的x,y值为子View的相对位置,如果该子View不可及或者不在当前touch事件位置,则直接遍历下一个View
  • 判断子View是否是在点击位置的方法是:isTransformedTouchPointInView(float x, float y, View child,PointF outLocalPoint) //具体可以参考源码,实现并不复杂,是根据当前点是否在View的Bound之内确定的
1.10.5 在遍历子View中,如果找到目标View,调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) 分发事件到子View,如果该子View没有消费则依次调用下一个,如果返回为true即该子View消费了该事件,直接break,跳出循环,此过程还涉及以下代码:
  • 1.10.5.1.newTouchTarget = addTouchTarget(child, idBitsToAssign);//调用addTouchTarget()此方法内部会赋值mFirstTouchTarget,并且该TouchTarget是一个链表结构
  • 1.10.5.2. alreadyDispatchedToNewTouchTarget = true;//标记已经找到消费该事件的子View,并已经分发
1.10.6 如果遍历完毕或者没有进入遍历逻辑,然后没有子View消费该事件,mFirstTouchTarget==null,则调用 dispatchTransformedTouchEvent(),此时childView为null,会直接调用Super.dispatchTouchEvent()方法,此时逻辑进入View的dispatchTouchEvent()
1.10.7 如果遍历完毕或者没有进入遍历逻辑,但是如果之前mFirstTouchTarget已经赋值不为null,则后续事件下发代码直接执行到此处,即直接找到之前消费过该事件序列的View进行分发
1.10.8 对于View的事件分发逻辑,简单调用顺序如下:dispatchTouchEvent()–>onTouchEvent()–>performClick()–>OnclickListener.onClick(this) 详细的过程此处不再赘述

ViewGroup对应的具体代码及简要注释如下:

@ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
    boolean handled = false;
    if (onFilterTouchEventForSecurity(ev)) {//安全校验,主要是当Window被遮盖时drop当前MotionEvent
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;

        // Handle an initial down.
        if (actionMasked == MotionEvent.ACTION_DOWN) {//----1.10.1 当检测到是ACTION_DOWN的时候,说明是一个新的Touch序列到来,则清除之前所有状态值,以及之前保存的所有缓存的数据
            // Throw away all previous state when starting a new touch gesture.
            // The framework may have dropped the up or cancel event for the previous gesture
            // due to an app switch, ANR, or some other state change.
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        // Check for interception.
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {//----1.10.2如果该EVENT为DOWN或者mFirstTouchTarget!=null----
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//该标志位默认为false,可以通过子View的getParent().requestDisallowInterceptTouchEvent();方法进行设置
            if (!disallowIntercept) {//1.disallowIntercept 默认为false,即允许ViewGroup对事件进行拦截。
                intercepted = onInterceptTouchEvent(ev);//此时intercepted的值由onInterceptTouchEvent(ev)决定
                ev.setAction(action); // restore action in case it was changed
            } else {//2.否则后续不再拦截该系列的touch事件
                intercepted = false;
            }
        } else {//----1.10.3如果该Event的ACTION!=ACTION_DOWN并且mFirstTouchTarget为null----,即一个事件序列的ActionDown事件没有被子View消费掉,则说明没有子View处理该事件序列的其他事件,直接交由ViewGroup处理
            // There are no touch targets and this action is not an initial down
            // so this view group continues to intercept touches.
            intercepted = true;
        }

        // If intercepted, start normal event dispatch. Also if there is already
        // a view that is handling the gesture, do normal event dispatch.
        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }

        // Check for cancelation.
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;

        // Update list of touch targets for pointer down, if needed.
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        if (!canceled && !intercepted) {//----1.10.3如果事件没有取消且没有拦截,只有在1.disallowIntercept为默认且onInterceptTouchEvent(ev)为true时,不再去分发ActionDown事件;2.事件为一个Touch事件非ACTION_DOWN且之前的ACTION_DOWN事件没有子View消费时,也不再进入此逻辑

            // If the event is targeting accessiiblity focus we give it to the
            // view that has accessibility focus and if it does not handle it
            // we clear the flag and dispatch the event to all children as usual.
            // We are looking up the accessibility focused host to avoid keeping
            // state since these events are very rare.
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                    ? findChildWithAccessibilityFocus() : null;

            //----如果是MotionEvent.ACTION_DOWN 则为新的touch事件序列,需要重新遍历子View并分发该事件,直到找到目标View,并且消费了该Action。TODO 是否所有该Action位置上的View都可以收到ActionDown事件?
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                final int actionIndex = ev.getActionIndex(); // always 0 for down
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                        : TouchTarget.ALL_POINTER_IDS;

                // Clean up earlier touch targets for this pointer id in case they
                // have become out of sync.
                removePointersFromTouchTargets(idBitsToAssign);

                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    // Find a child that can receive the event.
                    // Scan children from front to back.
                    final ArrayList<View> preorderedList = buildOrderedChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    for (int i = childrenCount - 1; i >= 0; i--) {//---1.10.4按添加顺序从后到前遍历子View,找出事件可及且位置是该点击位置所在的--View来进行分发
                        ....
                        //---如果该子View不可及或者不在当前触摸事件位置则continue,直接找下一个View----
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }
                        //---逻辑至此说明当前View满足可及且在当前Event所在位置
                        ...
                        resetCancelNextUpFlag(child);
                        //---1.10.5 调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign) 分发事件到子View如果该子View没有消费则依次调用下一个,如果返回为true即该子View消费了该事件,直接break,跳出循环
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            // Child wants to receive touch within its bounds.
                            mLastTouchDownTime = ev.getDownTime();
                            ...
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);//----添加目标TouchTarget,该方法中会赋值mFirstTouchTarget
                            alreadyDispatchedToNewTouchTarget = true;//置已经分发事件到目标target为true
                            break;
                        }

                        // The accessibility focus didn't handle the event, so clear
                        // the flag and do a normal dispatch to all children.
                        ev.setTargetAccessibilityFocus(false);//如果没有目标View没有消费该事件则重置相关标记
                    }
                    ....
                }
                ...
            }
        }


        // Dispatch to touch targets.
        if (mFirstTouchTarget == null) {//----1.10.6如果没有子View消费该事件,则调用 dispatchTransformedTouchEvent(),此时childView为null,会直接调用ViewGroup字节的dispatchTouchEvent()方法----
            // No touch targets so treat this as an ordinary view.
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {//----如果之前mFirstTouchTarget 已经赋值不为null,则后续事件下发代码直接执行到此处,即直接找到之前消费过该事件序列的View进行分发
            // Dispatch to touch targets, excluding the new touch target if we already
            // dispatched to it.  Cancel touch targets if necessary.
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    if (cancelChild) {
                        if (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                predecessor = target;
                target = next;
            }
        }
        在同一个序列的事件位置跑出当前View时,系统会触发一个Cancel事件到给当前View
        // Update list of touch targets for pointer up or cancel, if needed.
        if (canceled
                || actionMasked == MotionEvent.ACTION_UP
                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            resetTouchState();
        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
            final int actionIndex = ev.getActionIndex();
            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
            removePointersFromTouchTargets(idBitsToRemove);
        }
    }

    if (!handled && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
    }
    return handled;
}
至此我们就把事件分发的逻辑按照代码的执行顺序简单的过了一遍,也会对事件分发的流程有一个相对更深的理解,以上过程肯定会有错误和不恰当的地方,还请仅作为参考来看

猜你喜欢

转载自blog.csdn.net/M075097/article/details/79027195