CoordinateLayout 事件流程分析

以下Coor=CoordinateLayout

首先事件到达CoordinateLayout,回调dispatchTouchEvent,CoordinateLayout并没有重写该方法,于是在ViewGroup中:

   @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
.......
        boolean handled = false;

            // Check for interception.
            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); // 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;
            }

OK ,调用了onInterceptTouchEvent。这个方法在Coor类得到了 重写


 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        MotionEvent cancelEvent = null;

        final int action = MotionEventCompat.getActionMasked(ev);

        // Make sure we reset in case we had missed a previous important event.
        if (action == MotionEvent.ACTION_DOWN) {
            resetTouchBehaviors();
        }
        //关键方法
        final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);

        if (cancelEvent != null) {
            cancelEvent.recycle();
        }

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            resetTouchBehaviors();
        }

        return intercepted;
    }

关键方法:performIntercept(ev, TYPE_ON_INTERCEPT)

    private boolean performIntercept(MotionEvent ev, final int type) {
        boolean intercepted = false;
        boolean newBlock = false;

        MotionEvent cancelEvent = null;

        final int action = MotionEventCompat.getActionMasked(ev);

        final List<View> topmostChildList = mTempList1;
        getTopSortedChildren(topmostChildList);//以绘制的先后顺序取出子view(默认情况下)

        // Let topmost child views inspect first
        final int childCount = topmostChildList.size();

        //遍历所有的子view
        for (int i = 0; i < childCount; i++) {
            final View child = topmostChildList.get(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final Behavior b = lp.getBehavior();//取得子view里所使用的behavior

            if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {//不为ACTION_DOWN 事件,那么
                // Cancel all behaviors beneath the one that intercepted. 如果有一个behavior对事件进行了拦截,就发送Cancel事件给后续的所有Behavior(其实这和一般的view的Intercept流程差不多)。假设之前还没有Intercept发生,那么所有的事件都平等地对所有含有behavior的view进行分发,现在intercept忽然出现,那么相应的我们就要对除了Intercept的view发出Cancel
                // If the event is "down" then we don't have anything to cancel yet.
                if (b != null) {
                    if (cancelEvent == null) {
                        final long now = SystemClock.uptimeMillis();
                        cancelEvent = MotionEvent.obtain(now, now,
                                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);构建ACTION_CANCEL事件
                    }
                     //根据传入的参数不同回调相应事件
                    switch (type) {
                        case TYPE_ON_INTERCEPT:
                            b.onInterceptTouchEvent(this, child, cancelEvent);//注意是cancelEvent!
                            break;
                        case TYPE_ON_TOUCH:
                            b.onTouchEvent(this, child, cancelEvent);
                            break;
                    }
                }
                continue;
            }


            if (!intercepted && b != null) {
                switch (type) {
                    case TYPE_ON_INTERCEPT:
                        intercepted = b.onInterceptTouchEvent(this, child, ev);
                        //注意intercepted变量在这里有可能被更改!所以是否有intercepted的发生,完全取决于轮询的behavior的方法onInterceptTouchEvent和下面的onTouchEvent是不是返回true,如果是,事件就会被他截取走,其他的behavior就不会收到进一步的事件,如果返回false,那么事件还会继续向其他behavior分发下去
                        break;
                    case TYPE_ON_TOUCH:
                        intercepted = b.onTouchEvent(this, child, ev);
                        break;
                }
                if (intercepted) {
                    mBehaviorTouchView = child;
                }
            }

            // Don't keep going if we're not allowing interaction below this.
            // Setting newBlock will make sure we cancel the rest of the behaviors.
            final boolean wasBlocking = lp.didBlockInteraction();
            final boolean isBlocking = lp.isBlockingInteractionBelow(this, child);
            newBlock = isBlocking && !wasBlocking;
            if (isBlocking && !newBlock) {
                // Stop here since we don't have anything more to cancel - we already did
                // when the behavior first started blocking things below this point.
                break;
            }
        }

        topmostChildList.clear();

        return intercepted;
    }

结束,这个时候就得看看Coor的子view们到底有没有behavior了,如果有就会被依次回调behavior里头的相关方法

然而AppBarLayout的behavior并没有重写b.onTouchEvent和b.onInterceptTouchEvent。所以按照默认实现是返回false,没有进行截取。

好了接下来ViewGroup开始依次调用子view的dispatchTouchEvent,就是正常的事件分发了,后面详细说这个。
如果最终没有子view接受事件,那么就把事件传回父类(就是viewgroup的父类 view)的dispatchTouchEvent,在那里会调用onTouchEvent方法,而该方法也在Coor里头做了重写,来看看

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        boolean cancelSuper = false;
        MotionEvent cancelEvent = null;

        final int action = MotionEventCompat.getActionMasked(ev);

//mBehaviorTouchView记录的是:如果事件被某个子view的behavior截取的话,那么这个view就记录在这个变量里头,以后的事件都会传递给他。注意条件判断是||,意味着如果存在这个记录,那么performIntercept()就根本不会被执行(因为已经被截取了嘛)!
下面就会看到这个的用处
        if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) {
            // Safe since performIntercept guarantees that
            // mBehaviorTouchView != null if it returns true
            final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams();
            final Behavior b = lp.getBehavior();
            if (b != null) {
                handled = b.onTouchEvent(this, mBehaviorTouchView, ev);
            }
        }

        // Keep the super implementation correct
        if (mBehaviorTouchView == null) {
            handled |= super.onTouchEvent(ev);
        } else if (cancelSuper) {
            if (cancelEvent == null) {
                final long now = SystemClock.uptimeMillis();
                cancelEvent = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
            }
            super.onTouchEvent(cancelEvent);
        }

        if (!handled && action == MotionEvent.ACTION_DOWN) {

        }

        if (cancelEvent != null) {
            cancelEvent.recycle();
        }

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            resetTouchBehaviors();
        }

        return handled;
    }

注意第一个if语 句里头的performIntercept方法
performIntercept(ev, TYPE_ON_TOUCH),这已经在上面分析过了这个函数,只是这里传入的参数是TYPE_ON_TOUCH 而上面传递的performIntercept(ev, TYPE_ON_INTERCEPT)。,参数不同进入的就是不同的switch case语句了.

整个流程下来,关键的就是两次的performIntercept调用,第一次是在作为ViewGroup分发事件之前进行一次performIntercept,第二次是在作为一个view消费事件之前,也会进行一次

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值