android点击事件在activity中的传递流程

本文详细介绍了Android点击事件的传递流程,从Linux中断事件到Android输入系统的处理,再到Activity、DecorView以及ViewGroup之间的传递。重点解析了ViewGroup的dispatchTouchEvent方法,阐述了事件如何在视图层次中分发、拦截和消费的过程。

概述

当手指点击屏幕时候,linux就会接收到对应的事件中断,然后将这些中断事件包装成输入事件写入到相应的设备节点中,android输入系统所进行的操作大致来说就是监控这些设备节点,当有数据可读时候就读取这些数据,将其翻译成点击事件,然后寻找到合适的窗口,将其派发下去。

一个点击事件的传递流程

当产生一个点击事件后,首先在ViewRootImpl的WindowInputEventReceiver中接收到事件,然后将其传递到DecorView中,在activity中绕一圈继续回到DecorView开始真正的向下级控件传递,通过层层ViewGroup的传递最终到达View中,如果在ViewGroup中被拦截,或者传递到了View中,但是View并没有处理消费这次点击事件,就会层层返回false,最终在回到activity中,由activity调用activity中的onTouchEvent消费本次点击事件,流程如下图:
在这里插入图片描述
WindowInputEventReceiver的初始化

WindowInputEventReceiver继承自InputEventReceiver,用于接收从输入管理系统派发来的点击事件,在acivity界面显示过程中被初始化,其初始化过程如下图:
在这里插入图片描述
下是依据流程图中的具体代码细节截取:

ActivityThread.java

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
        初始化activity
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            ...
        }

        try {
            
                ......
                调用attach,在其中进行窗口、窗口管理、回调的初始化
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

                ......
    }
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,
            Window window, ActivityConfigCallback activityConfigCallback) {
    
        初始化mWindow、设置回调,初始化WindowManager
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        mUiThread = Thread.currentThread();

        ......
        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();

        mWindow.setColorMode(info.colorMode);

    }

attach运行完成会回到activity中的OnCreate

                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }

在oncreate中会进行界面布局文件的设置setContentView

    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

PhoneWindow中主要进行DecorView和mContentParent的初始化,和布局文件的inflate操作

    public void setContentView(int layoutResID) {

        if (mContentParent == null) {
        初始化mDecor和mContentParent
            installDecor();
        } 

        ......
        解析布局,添加到mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        ......
    }

布局文件解析完成后,会在ActivityThread中进入handleResumeActivity,在这里会完成布局文件的显示

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ......
        
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
              
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    将依据初始化完成的decorview添加到窗口中
                    wm.addView(decor, l);
                } else {
                    
                }
            }
            ......
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
        ......
    }
WindowManagerImpl.java

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }
WindowManagerGlobal.java

    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ......

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);


            try {
            调用ViewRootImpl完成事件接收器的初始化
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
               ......
            }
        }
    }
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ......
        if (mInputChannel != null) {
            if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
            mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
        .....
        
    }

至此,activity的界面显示完成,接收监听也已经初始化完成。

点击事件的传递

从ViewRootImpl开始后的点击事件传递过程与第一张图一致,通过一些列流转最终将事件传递至decorview中,在ViewRootImpl的setview中初始化了多个InputStage:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        ......
        初始化输入管道
        CharSequence counterSuffix = attrs.getTitle();
         mSyntheticInputStage = new SyntheticInputStage();
        InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
        InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
        InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
        InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
        InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
        InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

        mFirstInputStage = nativePreImeStage;
        mFirstPostImeInputStage = earlyPostImeStage;       .....
        
    }

事件接收器入口如下:

@Override
        public void onInputEvent(InputEvent event, int displayId) {
            enqueueInputEvent(event, this, 0, true);
        }
    void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        adjustInputEventForCompatibility(event);
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

        //从事件队列总取出事件,设置为即将发送事件
        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();
        }
    }
    void doProcessInputEvents() {
        // 依次发送队列中的所有事件
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);

            long eventTime = q.mEvent.getEventTimeNano();
            long oldestEventTime = eventTime;
            if (q.mEvent instanceof MotionEvent) {
                MotionEvent me = (MotionEvent)q.mEvent;
                if (me.getHistorySize() > 0) {
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
            }
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
            //发送单次事件
            deliverInputEvent(q);
        }

    private void deliverInputEvent(QueuedInputEvent q) {
        ......
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        
        //这里的stage是ViewPostImeInputStage
        if (stage != null) {
            handleWindowFocusChanged();
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }

最终进入了ViewPostImeInputStage下的processPointerEvent:

        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            //这里的mView就是decorview,在这里将事件传递给了decorview
            boolean handled = mView.dispatchPointerEvent(event);
            ......
        }

这次ViewRootImpl完成了事件的接收并将事件传递给了DecorView

DecorView的继承关系如下:
View
---->ViewGroup
-------->FrameLayout
------------>DecorView
在View中dispatchPointerEvent方法如下:

    public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }
DecorView.java

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //这里的cb就是activity,在activity的attach中,有如下语句:mWindow.setCallback(this);
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }
Activity.java

    public boolean dispatchTouchEvent(MotionEvent ev) {
        //提供一个空方法需要用户重载,便于在事件向view传递前做操作
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        //如下开始将事件向view传递,如果最终返回了true表示事件被消费,就此返回,如果返回了false,表示事件没有被消费掉,需要activity来处理。</br>
        //则调用activity的onTouchEvent来消费本次事件
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
    
    public boolean onTouchEvent(MotionEvent event) {
    //如果点击在窗口区域之外,就结束该activity
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }
    
        public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        final boolean isOutside =
                event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
                || event.getAction() == MotionEvent.ACTION_OUTSIDE;
        if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
            return true;
        }
        return false;
    }

phonewindow中再次将事件传给了decorview

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        //将事件传递给decorview
        return mDecor.superDispatchTouchEvent(event);
    }

DecorView中直接调用了父类的dispatchTouchEvent

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

事件传递的重点都在viewgroup中的dispatchTouchEvent中,流程如下:
在这里插入图片描述
代码解析如下:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        ......
        //点击事件是否已经被处理的标记
        boolean handled = false;
        //如果是模糊界面,比如有其他view覆盖在了这上面,则不进行传递
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                //action_down事件清除之前留下的touchtarget和touchstate
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // 检查是否允许拦截
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action);
                } else {
                    intercepted = false;
                }
            } else {
                //没有touch targets,或者不是action_down就拦截
                intercepted = true;
            }

            //如果被拦截,则启动正常事传递。 或者已经存在一个
            //正在处理手势的视图,执行正常的事件调度。
            if (intercepted || mFirstTouchTarget != null) {
                ev.setTargetAccessibilityFocus(false);
            }

            //检查是否cancle
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            //事件已经处理标记
            boolean alreadyDispatchedToNewTouchTarget = false;
            //不是cancle事件,且不允许拦截,则开始向下级子view传递
            if (!canceled && !intercepted) {

                //如果事件针对可访问性焦点,我们将其提供给具有可访问性焦点的视图,如
                //果它不处理它,我们清除标志并像往常一样将事件发送给所有子节点。
                //我们正在查找以可访问性为中心的主机以避免保持状态,因为这些事件非常罕见
                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;

                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;

                    //清除之前的touch target
                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        //从前到后的寻找一个可以处理这个事件的view
                        //构建子view的列表
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        //从前到后的寻轮从子view列表中所有的view
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            //获得接收事件的子view
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                            //如果已经存在一个获取了辅助焦点的view,我们希望它第一个
                            //接收事件,如果它不是第一个子view,我们就开始正常的调度继续轮询
                            if (childWithAccessibilityFocus != null) {
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }
                            //如果该view不可以接收事件,不是visibale状态,或者该点击point已经超出了该view的范围,则重新进行下一次轮询
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            //从第一个TouchTarget开始一直向后查找,直到找到给view对应的touchtarget
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                //target不为空,说明该child已经在接收事件了,结束循环
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                            //重置PFLAG_CANCEL_NEXT_UP_EVENT位,应该是标记取消下次发送
                            resetCancelNextUpFlag(child);
                            //传递事件给子view
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                //如果ziview处理了该事件,就记录下相关信息,将事件已经被处理标记置为true。
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                            //辅助焦点未处理事件,清除它
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }

                   ......
                }
            }

            //
            if (mFirstTouchTarget == null) {
                //将自身视作接收事件的view继续传递,最终调用这一层viewgroup的ontouchevent
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                
                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;
                        //传递cancle时间到子view中
                        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;
                }
            }

           ......
        }

       ......
        return handled;
    }
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        //如果传递的事件是cancle则不需要进行转换操作,直接传递至该ViewGroup的super类View或者子view中
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                //传给自己的父类中的事件传递
                handled = super.dispatchTouchEvent(event);
            } else {
                //传递给子view中的事件传递
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // 计算传递pointer的数量
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

       ......
        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);

                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }
        
        //传递transformedEvent事件
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }

        //完成
        transformedEvent.recycle();
        return handled;
    }

View中的dispatchTouchEvent解析如下:

    public boolean dispatchTouchEvent(MotionEvent event) {
        //如果时间需要首先给辅助焦点的view来处理
        if (event.isTargetAccessibilityFocus()) {
            //检测是否有可以正常处理辅助焦点的设备
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            //开始正常事件调度
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

      ......

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }
        //如果当前不是模糊界面,就开始处理
        if (onFilterTouchEventForSecurity(event)) {
        //如果是scroolbar拖拽事件,则按照bar拖拽处理后返回true给上级视图
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //已经添加了点击回调,则回调后返回true给上级视图
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            //否则就调用该view的onTouchEvent然后返回true给上级视图
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

       ......

        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

ViewGroup中的事件传递机制,概括起来就是:
1.如果允许拦截,就在这一层的viewgroup中调用onTouchEvent消费该事件,然后向上级返回true
2.如果不允许拦截,就从上到下依次向下级视图传递,下级视图可以是ViewGroup也可以是View,接收从下级视图的返回值,如果调用dispatchTransformedTouchEvent返回的是false说明下级子视图未处理,需要自己来处理,可以在这一层消费掉,然后向上级视图或者activity返回true告知其事件已经消费,如果从子视图返回的true说明子视图消费了本次点击事件,本视图继续向上一级视图返回true,告知其事件已经消费。
3.如果向子view中返回了false,并且自己这次层viewgroup也无法消费该事件,则继续向上一级视图返回false,由上一级视图来消费,直至传递到activity中,activity根据条件来决定是否需要finish该界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值