IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android事件分发初探 -> 正文阅读

[移动开发]Android事件分发初探

一、概述

1.1 事件分发概述

**答:**当用户触摸屏幕(ViewViewGroup派生的控件),将产生点击事件(Touch事件)。Touch事件的相关细节(发生触摸的位置时间等)被封装成MotionEvent对象,系统需把这个事件传递给一个具体的 View 去处理(消费)以及处理(消费)的整个过程。**即事件传递的过程及处理的整个过程,**其可能经过的对象有最上层Activity,中间层ViewGroup,最下层View。

1.2 事件的类型

事件类型具体动作
MotionEvent.ACTION_DOWN按下View(所有事件的开始)
MotionEvent.ACTION_UP抬起View(与DOWN对应)
MotionEvent.ACTION_MOVE滑动View
MotionEvent.ACTION_CANCEL结束事件(非人为原因)

1.3 什么是事件序列?

在这里插入图片描述

从手指接触屏幕 至 手指离开屏幕,这个过程产生的一系列事件是同一个事件序列。
一般情况下,事件序列以DOWN事件开始、UP事件结束,中间有无数的MOVE事件.

1.4 事件分发过程中共同协作的方法。

答:dispatchTouchEvent()onInterceptTouchEvent()onTouchEvent()

dispatch:派遣、发出、传递。
intercept:拦截

方法作用调用时刻
dispatchTouchEvent()分发(传递)点击事件点击事件(Touch)传递给当前View时,dispatchTouchEvent()就会被调用。
onTouchEvent()处理点击事件在dispatchTouchEvent()内部调用
onInterceptTouchEvent()判断是否拦截了某个事件(只存在于ViewGroup之中)在ViewGroup的dispatchTouchEvent()内部调用

1.5 事件在哪些对象之间传递?传递的顺序是什么?

答:当产生点击事件后会先由Activity来处理,在ActivityViewGroupView之间传递。

1.6 事件的传递过程

1.由上而下的传递过程

当一个ViewViewGrouponInterceptTouchEvent()返回true,表示它要拦截这个MotionEvent,会调用它的dispatchTouchEvent(),返回false则将MotionEvent传递给子元素的dispatchTouchEvent。最后传给最底层View,无法再向下传递,就由底层ViewonTouchEvent()处理。

2.由下而上的传递过程

如果onTouchEvent()返回true表示该View处理了,处理后逐层向dispatchTouchEvent()返回直至结束,返回false则代表没处理,继续向上层ViewonTouchEvent()传递,最后由Activity的onTouchEvent()处理,不论处理结果是什么都结束事件分发。

1.7 ViewGroup怎么通过dispatchTouchEvent()把事件传递给自己的onTouchEvent?

答:dispatchTouchEvent()return true或false都不行,只能在其内部通过Interceptor()将事件拦截并调用自己的onTouchEvent(),因此ViewGroupdispatchTouchEvent()super默认实现就是去调用此ViewGrouponInterceptTouchEvent()进行拦截。

1.8 View有自己的dispatchTouchEvent()方法吗?

答:有,但View没有onInterceptTouchEvent()来判断是将事件传给子View还是拦截,因此View调用super.dispatchTouchEvent() 时默认把事件传给自己的onTouchEvent()处理(拦截)。dispatchTouchEvent()中返回false就回溯父元素的onTouchEvent()由父元素处理。

1.9 dispatchTouchEvent()中希望让自己的onTouchEvent()处理怎么操作?

答:默认处理super.dispatchTouchEvent()会调用自己的onInterceptTouchEvent(),在其中return true表示拦截就会把MotionEvent分发给自己的onTouchEvent()处理。

1.10 dispatchTouchEvent()中希望传给子View怎么操作?

答:默认处理super.dispatchTouchEvent()会调用自己的onInterceptTouchEvent(),在其中return false(不拦截且不消费),就会将MotionEvent交给子类。

1.11 希望不做处理、终止向下传递、开始回溯,怎么操作?

答:在每一层View的onTouchEvent()中return false;

1.12 onTouch和onTouchEvent的区别是什么?

答:两个方法都是在View.dispatchTouchEvent()中调用,源码如下:

public boolean dispatchTouchEvent(MotionEvent event) {
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
            mOnTouchListener.onTouch(this, event)) {
        return true;
    }
    return onTouchEvent(event);
}

可以看到onTouch()优先于onTouchEvent()执行,如果onTouch方法中返回true将事件消费,onTouchEvent就不会执行。

onTouch()的执行需要mOnTouchListener不为空且当前点击的控件为ENABLED,对于非ENABLED控件(不可点击控件),我们如果想要监听其touch事件应该重写onTouchEvent(),因为它的onTouch()永远不会得到执行!

1.13 事件分发传递规则

  1. 一般情况下,一个事件序列只能被一个View拦截消耗,一旦拦截事件序列中所有事件会交给此View处理,不能由两个View同时处理,除非在onTouchEvent()将MotionEvent传递给其他View。
  2. 一个ViewonTouchEvent()返回了false,那么同一事件序列都不会再交给此View处理,而是重新交给父元素的onTouchEvent()
  3. 一旦某个View决定拦截,此View的onInterceptTouchEvent()不会再被调用,不会再次询问去询问它是否拦截。
  4. dispatchTouchEvent()onTouchEvent() 都return true,事件传递就到达了终点(被消费),不会再继续传递。
  5. dispatchTouchEvent()事件分发时,只有前一个事件(如ACTION_DOWN)返回true,才会收到后一个事件(ACTION_MOVE和ACTION_UP)
  6. 若对象(Activity、ViewGroup、View)dispatchTouchEvent()分发事件后消费了事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP!
  7. 若对象(Activity、ViewGroup、View)onTouchEvent()处理了事件(返回true),那么ACTION_MOVE、ACTION_UP的事件从上往下传到该View后就不再往下传递,而是直接传给自己的onTouchEvent(),结束本次事件传递过程!

二、事件分发源码分析

2.1 流程1:Activity的事件分发机制

Android的事件分发机制首先将MotionEvent传递给Activity的dispatchTouchEvent()进行事件分发。

	
		public boolean dispatchTouchEvent(MotionEvent ev) {
        /*if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }*/
/***交给Activity附属的Window分发**  
如果getWindow().superDispatchTouchEvent(ev)为true 代表事件循环结束
Activity的dispatchTouchEvent就返回ture
*/
//如果getWindow().superDispatchTouchEvent(ev)为false 意味着没处理,执行onTouchEvent()
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

Window是一个抽象类,其DispatchTouchEvent()也是抽象方法,该类可以控制顶级View的外观和行为策略。该类唯一实现是PhoneWindow,其中有一个内部类DecorView是一个ViewGroupPhoneWindow中处理点击事件:

	public boolean superDispatchTouchEvent(MotionEvent event) {
			// mDecor 是 顶级View(DecorView)的实例对象
      return mDecor.superDispatchTouchEvent(event);
  }

顶级View,即在Activity中通过setContentView()设置的View,也叫根View,此处调用的是ViewGroup的dispatchTouchEvent()方法,从而实现了事件由Activity→ViewGroup的dispatchTouchEvent()的过程。

流程图和方法图非常详尽:

在这里插入图片描述

ViewGroupdispatchTouchEvent()什么时候返回true / false?接下来ViewGroup的事件分发流程会进行说明。

2.2 流程2:ViewGroup的事件分发机制

由Activity的事件分发机制可知,Activity.dispatchTouchEvent()getWindow().superDispatchTouchEvent(ev)super.dispatchTouchEvent(MotionEvent event)实现了Activity->ViewGroup的传递,ViewGroup的事件分发机制也由dispatchTouchEvent()进行。

		public boolean dispatchTouchEvent(MotionEvent ev) {
			      ...
/*调用**ViewGroup的onInterceptTouchEvent()**进行判断,
返回true表示ACTION_DOWN被VIEWGROUP拦截,返回false则表示事件继续传递给子View*/
            
						final boolean intercepted;//intercepted变量表示**是否**拦截该次事件
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = **onInterceptTouchEvent(ev)**;
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }
						if (!canceled && !intercepted) { // 没取消也不拦截,即是个有效的touch事件
                if (actionMasked == MotionEvent.ACTION_DOWN // 第一个手指down
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) // 接下来的手指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 View[] children = mChildren;

                        final boolean customOrder = isChildrenDrawingOrderEnabled();
                        // 从最后一个向第一个遍历ViewGroup下所有子View
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder ?
                                    getChildDrawingOrder(childrenCount, i) : i;
                            final View child = children[childIndex];
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                continue; // 不满足这2个条件直接跳过,看下一个child
                            }

                            // child view能receive touch事件而且touch坐标也在view边界内

                            newTouchTarget = getTouchTarget(child);// 查找child对应的TouchTarget
                            if (newTouchTarget != null) { // 比如在同一个child上按下了多跟手指
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.

                                //子View已经在自己的范围内得到了触摸。
                                //除了它正在处理的那个,给它一个新的指针。
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break; // newTouchTarget已经有了,跳出for循环
                            }

                            resetCancelNextUpFlag(child);
                            // 将此事件交给child处理
                            // 有这种情况,一个手指按在了child1上,另一个手指按在了child2上,以此类推
                            // 这样TouchTarget的链就形成了
                            // 进行子View的分发
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                mLastTouchDownIndex = childIndex;
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                // 如果处理掉了的话,将此child添加到touch链的头部
                                // 注意这个方法内部会更新 mFirstTouchTarget
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true; // down或pointer_down事件已经被处理了
                                break; // 可以退出for循环了。
                            }
                        }
                    }

ViewGroup会用**onInterceptTouchEvent()**进行判断是否对该事件进行拦截,默认不拦截,如果拦截就用的onInterceptTouchEvent()会向ViewdispatchTouchEvent()分发。

ViewGroup的onInterceptTouchEvent()源码

		public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }

ViewGroup的dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)源码

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        
				...
				final boolean handled;

       //仅分析核心代码
        // Perform any necessary transformations and dispatch.
        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());
            }

	//分发给子View的dispatchTouchEvent
            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }

遍历ViewGroup的子View或子ViewGroup,判断此次点击事件触摸区域是否属于它的子View或子ViewGroup的区域,如果属于子View区域就调用子View的dispatchTouchEvent(),即传递给子View处理,如果属于子ViewGroup,继续遍历子ViewGroup子视图,直到找到处理事件的View。如果遍历到最后还找不到就回调上一层onTouchEvent()直到Activity。

流程图:

在这里插入图片描述

2.3 流程3:View的事件分发机制

View的事件分发同样由dispatchTouchEvent()开始

		public boolean dispatchTouchEvent(MotionEvent event) {
        ...

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }
				...
        if (onFilterTouchEventForSecurity(event)) { // 一般都成立
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
//先判断是否设置OnTouchListener,
//如果OnTouchListener中onTouch()返回true,onTouchEvent()就不会被调用,
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
//从这里看出,如果既设置了onTouchListener又设置了onClickListener,
//如果OnTouchListener中onTouch()返回true,onTouchEvent()不会被调用,onClickListener也不会被触发
//onTouch中每次收到Touch事件,但onClickListener只在ACTION_UP来时触发
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

View的onTouchEvent()

		public boolean onTouchEvent(MotionEvent event) {// View对touch事件的默认处理逻辑
        ...
        if ((viewFlags & ENABLED_MASK) == DISABLED) {// DISABLED的状态下
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);//如果之前是Pressed状态则复原
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return clickable;
        }
        if (mTouchDelegate != null) {
						//如果有mTouchDelegate,优先交给它处理
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;// 处理了返回true,否则向下进行
            }
        }

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if (!clickable) {
                        removeTapCallback();
                        removeLongPressCallback();
                        mInContextButtonPress = false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break;
                    }
										// 如果外围有可以滚动的parent的话,当按下时会设置这个标志位
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
										// 按下了或者预按下了
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                        }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClickInternal();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

                case MotionEvent.ACTION_DOWN:
                    if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                        mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                    }
                    mHasPerformedLongPress = false;

                    if (!clickable) {
                        checkForLongClick(
                                ViewConfiguration.getLongPressTimeout(),
                                x,
                                y,
                                TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
                        break;
                    }

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {// 如果是在可以滚动的container里面的话
                        mPrivateFlags |= PFLAG_PREPRESSED;// 设置PREPRESSED标志位
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);// 否则直接显示pressed feedback
                        checkForLongClick(// 启动长按监测
                                ViewConfiguration.getLongPressTimeout(),
                                x,
                                y,
                                TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL://针对CANCEL事件,恢复各种状态,移除各种callback
                    if (clickable) {
                        setPressed(false);
                    }
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if (clickable) {
                        drawableHotspotChanged(x, y);
                    }

                    final int motionClassification = event.getClassification();
                    final boolean ambiguousGesture =
                            motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE;
                    int touchSlop = mTouchSlop;
                    if (ambiguousGesture && hasPendingLongPressCallback()) {
                        if (!pointInView(x, y, touchSlop)) {// 如果移动到view的边界之外,
                            // The default action here is to cancel long press. But instead, we
                            // just extend the timeout here, in case the classification
                            // stays ambiguous.
                            removeLongPressCallback();
                            long delay = (long) (ViewConfiguration.getLongPressTimeout()
                                    * mAmbiguousGestureMultiplier);
                            // Subtract the time already spent
                            delay -= event.getEventTime() - event.getDownTime();
                            checkForLongClick(
                                    delay,
                                    x,
                                    y,
                                    TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS);
                        }
                        touchSlop *= mAmbiguousGestureMultiplier;
                    }

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, touchSlop)) {// 如果移动到view的边界之外,
                        // Outside button
                        // Remove any future long press/tap checks
                        removeTapCallback();// 则取消Tap callback,这样当你松手的时候onClick不会被触发
                        removeLongPressCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {// 当已经是按下状态的话
                            setPressed(false);// 恢复按下状态
                        }
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    }

                    final boolean deepPress =
                            motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS;
                    if (deepPress && hasPendingLongPressCallback()) {
                        // process the long click action immediately
                        removeLongPressCallback();
                        checkForLongClick(
                                0 /* send immediately */,
                                x,
                                y,
                                TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS);
                    }

                    break;
            }

            return true;// 最后返回true,表示对touch事件被处理,被消费
        }

        return false;// 既不能单击也不能长按的View,返回false,表示不处理touch事件
    }

在这里插入图片描述

返回值分析:

在这里插入图片描述

  1. 如果getWindow().superDispatchTouchEvent()返回true,也就是ViewGroup.dispatchTouchEvent()返回true,则方法结束,Activity的dispatchTouchEvent()也返回true,代表点击事件顺利从Activity传递到ViewGroup,ViewGroup分发后事件在其中被消费,Activity的分发任务结束,如果Activity的onTouchEvent()返回false,则Activity的dispatchTouchEvent()返回false,则代表事件没有被消费。
  2. 如果getWindow().superDispatchTouchEvent()方法返回false,则执行ActivityonTouchEvent()方法,无论该方法返回true还是false,都表示此次事件分发都结束,ture表示点击事件在Window边界外(事件被Activity消费),falseWindow边界内(事件不被处理,不算Activity消费)。

三、事件分发三个方法的流程图

3.1 dispatchTouchEvent()流程图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.2 onInterceptTouchEvent()流程图

需要注意:只有ViewGroup有这个方法,ActivityView都没有

在这里插入图片描述

3.3 onTouchEvent()流程图

在这里插入图片描述

在这里插入图片描述

四、事件传递的几种情况

4.1 默认情况

不对控件里的方法(dispatchTouchEvent()onTouchEvent()onInterceptTouchEvent())进行重写。

需要注意的是:onTouchEvent()如果对DOWN事件返回了false,那么将不会再接收处理事件列的其他事件。但ViewGrouponInterceptTouchEvent()DOWN事件返回了false后后续事件仍会传递到它的onInterceptTouchEvent()

在这里插入图片描述

4.2 处理事件

如果View V希望处理点击事件,那么设置View V(Clickable)或重写onTouchEvent()返回true

事件传递情况

  • DOWN事件被传递给VonTouchEvent()并返回true,表示V要处理该事件
  • V正在处理该事件,所以不会将DOWN传递给ViewGroupActivityonTouchEvent()
  • 事件列中其他事件也会传递给VonTouchEvent()
  • 如下图,也就是逐层向dispatchTouchEvent()返回直至结束。

在这里插入图片描述

4.3 拦截DOWN事件

如果希望ViewGroup vg处理该点击事件,重写其onInterceptTouchEvent()返回true,onTouchEvent()返回true。

  • onInterceptTouchEvent()返回true表示事件不再向下传递
  • 自身onTouchEvent()处理事件,不会传递给Activity的onTouchEvent()
  • 事件列内其他事件都传递给vg的onTouchEvnet(),且不会再传递给vgonInterceptTouchEvent(),因为一旦onInterceptTouchEvent()返回true就再也不会被调用。
  • 逐层向dispatchTouchEvent()返回,结束事件分发。

在这里插入图片描述

4.4 拦截事件列中间事件

如果ViewGroup vg拦截了一个MOVE,那么该事件会变为CANCEL并传递给在处理该事件的View,且不传递给vgonTouchEvent()vgonTouchEvent()会接收新到来的MOVE事件。

例如vg的子View v处理DOWN事件(v的onTouchEvent()返回true),但vg拦截了MOVE事件(vgonInterceptTouchEvent()返回true拦截了MOVE),这个MOVE会变成CANCEL传递给v的onTouchEvent(),后续新来的MOVE会传递给vg的onTouchEvent(),而不是vg的onInterceptTouchEvent()。也就是后续事件直接由vg的onTouchEvent()处理。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-27 10:01:16  更:2021-11-27 10:01:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:04:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码