V ie w 内默认消息派发过程

V ie w 内默认消息派发过程

public boolean dispatchTouchEvent(MotionEvent event) {
        /**
         * 一 调 用 onFilterTouchEventForSecurity()处理窗口处于模糊显示状态下的消息。所谓的模糊显示是
         * 指 ,应用程序可以设置当前窗口为模糊状态,此时窗口内部的所有视图将显示为模糊效果。这样做的目
         * 的 是 为 了 隐 藏 窗 口 中 的 内 容 , 对 于 其 中 各 个 视 图 而 言 , 可 以 设 置 该 视 图 的
         * HLTER_TOUCHES__WHEN_OBSCURED标识,如存在该标识,则意味着用户希望不要处理该消息。
         */
        if (!onFilterTouchEventForSecurity(event)) {
            return false;
        }
        /**
         * 二 、回调视图监听者的onTouchO函数,如果监听者消耗了该消息,则直接返回
         */
        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        /**
         * 调 用 onTouchEventO,应用程序可以重载该函数,但如果没有重载的话,该函数内部有默认的
         * 执行方式。
         */
        return onTouchEvent(event);
    }
public boolean onTouchEvent(MotionEvent event) {
        final int viewFlags = mViewFlags;
        /**
         * 一 、判断该视图是否为disable状态,如果是,什么都不干,返回true,即消耗该消息。
         */
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }
        /**
         * 二 处理消息代理TouchDelegate。所谓的消息代理是指,可以给某个View指定一个消息处理代
         * 理,当 View收到消息时,首先将该消息派发给其代理进行处理。如果代理内部消耗了该消息,则 View
         * 不需要再进行任何处理;如果代理没有处理,则 View继续按照默认的逻辑进行处理。源码中该类的注
         * 释中说,该类的目的是为了扩大点击区,意思很简单,
         * 在一般情况下,只有当用户点击到该区域时,该 View
         * 对象才能处理Touch消息。然而有时却希望实际的点击区域能够大于View本身的区域,如虚线区所示,
         * 这种情况一般发生在该View本身周围没有其他视图时,为了提高点击的准确率故意设置。这个想法是
         * 好的,但是源码中却没有真正实现这个目的,因为要实现这个目的,必须在将消息匹配到对应窗口的判
         * 断中使用代理视图的大小,而不能使用视图本身的大小,否则,该视图将因为点击位置没有落到视图区
         * 而被忽略。然而源码中却没有经过这个判断,所 以 TouchDelegate形同虚设。
         */
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        /**
         * 判断该视图是否是可以点击的,如果不可点击,则直接返回false,即不处理该消息。否则,
         * 真正开始执行触摸消息的默认处理逻辑,该逻辑中分别处理了 ACTION4DOWN、 MOVE和 UP消息,
         * 具体过程如下:
         * 1)在 ACTION_DOWN消息中给 mPrivateFlags变 量 添 加 PRESSED标 识 ,并将变量
         * mHasPerformLongPress置 为 false,然 后 启 动 ta p 监 测 , 即 发 送 一 个异
         * 步 延 迟 消 息 ,延迟时间为
         * ViewConfigration.getTapTimeout()。
         *
         * 2)对于触摸消息而言,消息本身没有repeat的属性,这与按键消息有所不同,一次触摸消息只有
         * 一 个DOWN消息,接下来就是连续的MOVE消息,并最终以UP消息结束。因此,本步骤中对MOVE
         * 消息进行处理。具体逻辑包括,判断是否移动到了视图区以外,如果是,则删除 tap或 者 longPress的
         * 监测,并清除mPrivateFlags中的PRESSED标识,然后调用refreshDrawableState()刷新视图的状态,这
         * 将导致对背景根据状态进行重绘。
         *
         * 这个处理逻辑有点问题。在一般的GUI系统中,比如HTC magic手机,或者苹果系统,或者Windows
         * 系统,当用户按下某个按钮时,如果移动光标到按钮外,此时按钮会从高亮恢复到普通状态,但如果用
         * 户把光标再移回到该按钮上时,按钮又会重新变为高亮。然而在目前的Android系统中,代码的设计是,
         * 当用户把光标再次移回到视图区时,视图不会重新获得聚焦,实际测试结果也是如此。
         * 3) 处 理ACTION_UP消息,代码中判断该UP消息是发生在前面所讲的哪一个监测时间段中,并据
         * 此进行不同的处理。
         * a .查看是否发生在pre-pressed时间段内,如果是,则给变量prepressed赋 值为true。
         * b .无论是发生在pre-pressed区还是发生在press区,都应该让该视图获取焦点,前提是该视图在
         * Touch模式下可以拥有焦点。
         * c .如果发生在press之后,即长按,则什么都不做,因为长按的异步消息处理中已经处理了长按消
         * 息 。 如 果 是 发 生 在 长 按 之 前 , 则 变 量 mHasPerformedLongPress为 false, 此 时 调 用
         * removeLongPressCallbackO移除还没有执行的长按监测消息。
         * d .判断变量focusTaken的局部变量是否为false,在一般情况下,该变量都是false, 因为一般情况
         * 下视图默认在Touch模式下是不能获得焦点的,所 以 requestFocus()会 返 回false。当然,只有当返回false
         * 的情况下,该 UP消息才能导致回调performClick()函数,否则,当 focusTaken为 true时,用户点击某
         * 个视图,该视图仅仅是获得焦点,必须再点击一次才能执行performClick()函数。因为再点击一次时,
         * 该视图已经获得了焦点,从而不会调用requestFocus(),所以变量focusTaken为初始值false。该步说明
         * 了 tap和 press动作的相同及区别,相同的地方在于它们都会引起视图聚焦或者执行performClick(),区
         * 别在 于tap不会改变视图的状态,而 press会把视图的状态改变为PRESSED。从功能的角度来看,两者
         * 没有本质区别,仅仅是反映的U I效果有少许差别而已。
         * 5、分别处理tap和 press动作。如 果 是press动作,则 清 除PRESSED标识,并改变视图状态;
         * 如果是tap,仅仅发送一个UnSetPressedState异步消息。
         */
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
                    if ((mPrivateFlags & 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 (!mHasPerformedLongPress) {
                            // 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)) {
                                    performClick();
                                }
                            }
                        }

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

                        if (prepressed) {
                            mPrivateFlags |= PRESSED;
                            refreshDrawableState();
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
                        /**
                         * 调用 removeTapCallBack()关闭tap检测
                         */
                        removeTapCallback();
                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    mPrivateFlags |= PREPRESSED;
                    mHasPerformedLongPress = false;
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    break;
                /**
                 * 处 理 ACTION—CANCEL消息,这里只需要清除PRESSED标识,刷新视图状态,然后再关闭
                 * tap监测即可。
                 */
                case MotionEvent.ACTION_CANCEL:
                    mPrivateFlags &= ~PRESSED;
                    refreshDrawableState();
                    removeTapCallback();
                    break;

                case MotionEvent.ACTION_MOVE:
                    final int x = (int) event.getX();
                    final int y = (int) event.getY();

                    // Be lenient about moving outside of buttons
                    int slop = mTouchSlop;
                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||
                            (y < 0 - slop) || (y >= getHeight() + slop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();

                            // Need to switch from pressed to not pressed
                            mPrivateFlags &= ~PRESSED;
                            refreshDrawableState();
                        }
                    }
                    break;
            }
            return true;
        }

        return false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值