view事件分发源代码详解

view事件分发
1,首先 viewgroup并没有重写onTouchEvent 方法,而viewgroup是view 的子类,所以view的onTouchEvent 方法也是veiwgroup的方法
2, view 没有onInterceptTouchEvent,方法,因为他不需要拦截事件,
首先看下 onDispatchTouchEvent方法源代码

    public boolean dispatchTouchEvent(MotionEvent event) {

        //记录最后的处理结果
         boolean result = false; 

        //这是一个验证事件完整性的一个类,一会会看到在本方法结束的地方也有这个类出现
        //防止event事件在分发过程中不一致,这个类还有记录的功能
        if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }
            //活动事件种类 
        final int actionMasked = event.getActionMasked();
            //如果是down 就停止
        if (actionMasked == MotionEvent.ACTION_DOWN) {
                 // Defensive cleanup for new gesture
            stopNestedScroll(); //停止view中嵌套的滑动
        }

            //view 为 enable,并且事件可以被scroll bar 处理掉时,返回true
            //表示事件被滑动所消耗掉了
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
         }
        //这个 li中保存了各种我们为view设置的listener 
         ListenerInfo li = mListenerInfo;
        //先调用OnTouchListener的 onTouch ,返回true表示事件被消耗了
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        //看到这里就知道,如果 onTouchListener的onTouch返回true,消耗掉了事件,就不会在回调
        //onTouchEvent了
         if (!result && onTouchEvent(event)) {
            result = true;
        }

        //表示事件没被处理,和之前的事件校验器相对应
        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        //如果抬起事件和取消事件,及down事件却没有消耗掉, 同样都要取消掉滑动
         if (actionMasked == MotionEvent.ACTION_UP ||
            actionMasked == MotionEvent.ACTION_CANCEL ||
            (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
         }
        //分发结束
         return result;
    }

    接下来看 ontouchEvent()

    这里有几种状态 : click press longclick longpass
    click 和 press 由up down 组成
    longpass 由 up down 组成,但是两个事件之间有延时,一定不能有move
    longclick 由 up down move 组成, move可有可无

    public boolean onTouchEvent(MotionEvent event) {
        //获取event的坐标,事件类型,及view的状态标志 mViewFlags,它通过位运算存储不同的状态
         final float x = event.getX();
         final float y = event.getY();
         final int viewFlags = mViewFlags;
         final int action = event.getAction();
        //view处于disable状态,能接受到事件,但是没有任何处理...
          if ((viewFlags & ENABLED_MASK) == DISABLED) {

             if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false); //传入false 表示还原之前 pass 状态
             }

       // 一个 view 是 disable 且 clickable 仍然会消耗事件,只是没有任何响应
             return (((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
         }

         if (mTouchDelegate != null) {
        //代理来理事件,会把事件交给调用代理的view
        //代理使view可以处理的事件的接触面积变大.但其实还是调用view自己的实际处理方法
             if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
         }
        //判断是否可点,是否可长按,上下文是否可点 任意为真就可以消耗事件,否则表示不消耗事件
          if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
        switch (action) {
             case MotionEvent.ACTION_DOWN: //这里调换了源代码的顺序

                mHasPerformedLongPress = false; //是否执行长按事件
                 //看是否view是butten类似的控件,能否消耗掉点击事件-不太明白
                if (performButtonActionOnTouchDown(event)) {
                    break;
                }

                //判断是否在滚动容器中
                boolean isInScrollingContainer = isInScrollingContainer();

                if (isInScrollingContainer) {
                    // //mPrivateFlags 设置为 PFLAG_PREPRESSED(pass的前状态,后边会用到)
                    mPrivateFlags |= PFLAG_PREPRESSED;

                    if (mPendingCheckForTap == null) {
                        //异步执行pass状态,及延时设置longClick状态
                        //longclick状态有他自己的延时时长,
                        mPendingCheckForTap = new CheckForTap();  
                    }
                    mPendingCheckForTap.x = event.getX();
                    mPendingCheckForTap.y = event.getY();
                    //延时设置pass及longclick状态.
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                } else { 不在滑动中,直接设置press 及longclick状态
                    // Not inside a scrolling container, so show the feedback right away
                    //不在滑动中,立刻设置为press状态,同时设置为longclick状态
                    setPressed(true, x, y);
                    //检查执行长按,longclick有他自己的延时时间
                    checkForLongClick(0, x, y);
                }
                break;

            case MotionEvent.ACTION_CANCEL://重置所有状态
                setPressed(false); //还原之前的press状态
                removeTapCallback();//还原 轻按状态
                removeLongPressCallback();//还原longclick状态
                mInContextButtonPress = false;
                mHasPerformedLongPress = false;
                mIgnoreNextUpEvent = false;
                break;
            case MotionEvent.ACTION_MOVE:
                //提醒子view(如果有)触摸点坐标变换了
                drawableHotspotChanged(x, y);

                //移动到了按钮外边
                if (!pointInView(x, y, mTouchSlop)) { 
                    // 移除tap回调
                    removeTapCallback();
                    //如果view为press状态
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                        //还原 longclick和 press 状态
                        removeLongPressCallback(); 
                        setPressed(false);
                    }
                }
            case MotionEvent.ACTION_UP:
                //是否为 prepress 状态
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;

                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                //press状态或者 prePass 状态

                    boolean focusTaken = false;
                    // view 获取焦点
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus(); //重新获取焦点
                    }
                    //之前为prepress 在up时就变更为press
                    if (prepressed) { 
                        setPressed(true, x, y);
                    }

                    //移除longPass 状态
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {

                        removeLongPressCallback(); 
                         if (!focusTaken) { //没有获取焦点 有可能表示view 不接受输入

                            //异步发送一个点击事件,调用 clicklistener 的 onclick 方法
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            //
                            if (!post(mPerformClick)) { 
                            //异步发送失败,则手动发送点击事件
                                performClick(); //调用click 方法
                            }
                        }
                    }

                    //一个异步还原press的 runnable类
                    if (mUnsetPressedState == null) {
                        mUnsetPressedState = new UnsetPressedState();
                    }
                     //异步还原pass 状态,如果失败就手动调用
                    if (prepressed) { 
                        postDelayed(mUnsetPressedState,
                                ViewConfiguration.getPressedStateDuration());
                    } else if (!post(mUnsetPressedState)) {
                        // If the post failed, unpress right now
                        mUnsetPressedState.run();
                    }
                    removeTapCallback(); //移除tap状态
                    mIgnoreNextUpEvent = false;
                    break;
                }
                return true;
            }
        return false;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值