Android中的事件分发剖析

事件分发在Android开发中无处不在,只要是控件都离不开事件的分发,事件就是控件的灵魂,本文剖析下的ACTIONDOWN事件分发原理;

从用户点击屏幕那一刻开始,在java层首先接触到事件的是Activity我们看下Activity中接收事件并分发的代码

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

第一个判断条件中的onUserInteraction(),是一个空实现,用来Activity接收的事件之后重写的方法,我们重点看第二个判断条件getWindow.superDispatchTouchEvent(),,我们都是知道window有一个唯一的子类phoneWindow,所以此方法实际上调用了phoneWindow的superDispatchKeyEvent(ev)方法如下图,


    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

我们可以看到此方法又调用了顶层的DecorView的superDispatchTouchEvent()方法,我们继续往下跟踪:

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

我们都知道DecorView是继承自FrameLayout,所以 super.dispatchTouchEvent(event),实际上是调用了ViewGroup的 dispatchTouchEvent(event)方法;

到这里我们可以做一个小结:用户点击屏幕以后,事件的传递顺序是:Activity---PhoneWindow---DecorView---ViewGroup---View

接下来我们先来看ViewGroup的dispatchTouchEvent方法,由于原码逻辑比较多,所以这里我进行了适当的抽取,只选择了比较核心的DOWN事件的传递

 public boolean disPatchTouchEvent(MotionEvent motionEvent) {
        TouchTarget newTouchTarget = null;
        boolean handled = false;
        boolean intercepted = onInterceptTouchEvent(motionEvent);
        int actionMasked = motionEvent.getActionMasked();
        if (actionMasked != MotionEvent.ACTION_CANAEL && !intercepted) {
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                final View[] children = mChildren;
                for (int i = children.length - 1; i >= 0; i--) {
                    View child = children[i];
                    if (!child.isContainer(motionEvent.getX(), motionEvent.getY())) {
                        continue;
                    }

                    if (dispatchTransformedTouchEvent(motionEvent, child)) {
                        handled = true;
                        newTouchTarget = addTouch(child);
                        break;
                    }

                    if (mFirstTouchTarget == null) {

                    }
                }
            }
        }
        return handled;
    }

    private TouchTarget mFirstTouchTarget;


    private TouchTarget addTouch(View child) {
        TouchTarget touchTarget = TouchTarget.obtain(child);
        touchTarget.next = mFirstTouchTarget;
        mFirstTouchTarget = touchTarget;
        return touchTarget;
    }


    public boolean dispatchTransformedTouchEvent(MotionEvent motionEvent, View 
     childView) {
        boolean isDispatch;
        if (childView != null) {
            isDispatch = childView.disPatchTouchEvent(motionEvent);
        } else {
            isDispatch = super.disPatchTouchEvent(motionEvent);
        }
        return isDispatch;
    }

    public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
        return false;
    }

我们可以看到dispatchTouchEvent里面,首先是根据onInterceptTouchEvent()方法来判是否要阻断点击事件,这里我默认返回是false也就是不阻断,然后开始循环遍历Group中的子控件,我们可以看到isContainer()方法,此方法实际上就是用来判断点击事件的范围是否在子控件的边界范围之内,如果在我们就确定此控件可以接受事件,如果不在则跳过,继续遍历下一个;当找到第一个可以接受事件的子View后,就开始执行dispatchTransformedTouchEvent方法,可以看到我们把接受事件的child对象传入到了此方法,然后调用childView的disPatchTouchEvent方法,如果childView是ViewGroup那么会继续调用上面disPatchTouchEvent方法继续遍历这个childView,相当于递归自己调用自己;如果childView是View,那么直接调用View的disPatchTouchEvent

    public boolean disPatchTouchEvent(MotionEvent motionEvent) {
        boolean result = false;
        if (mOnTouchListener != null && mOnTouchListener.onTouch(this, motionEvent)) {
            result = true;
        }
        if (!result && onTouchEvent(motionEvent)) {
            result = true;
        }
        return result;
    }

我们看到当事件来到view的dispatchTouchEvent以后,就会判断用户是否设置了onTouchListener,也就是为什么我们会说触摸监听的优先级会高于触摸事件的优先级, 如果触摸监听的回调方法onTouch返回了ture,那么result就不会就下面的onTouchEvent方法,如果返回用户没有返回false或者说onTouch方法返回false,那么就会继续调用onTouchEvent方法,

    public boolean onTouchEvent(MotionEvent motionEvent) {
        if (onClickListener != null) {
            onClickListener.onClick(this);
            return true;
        }
        return false;
    }

如果用户设置了点击事件,那么就代表用户消费了此事件,那么时间传递就结束,如果onTouchEvent返回false,相当于上面的childView的dispatchTouchEvent方法返回了false,相当于ViewGroup的dispatchTransformedTouchEvent方法返回了false那么就会走到

if (mFirstTouchTarget == null) {
   //这里面就又会调用
    dispatchTransformedTouchEvent(MotionEvent motionEvent, null)
}
public boolean dispatchTransformedTouchEvent(MotionEvent motionEvent, View childView) {
    boolean isDispatch;
    if (childView != null) {
        //
        isDispatch = childView.disPatchTouchEvent(motionEvent);
    } else {
        //相当于子View没有消费时间,那么就会调用父类的disPatchTouchEvent,
        //相当于又会调用View的disPatchTouchEvent方法,注意此时的view代表的ViewGroup
        //表示子View没有消费事件,所以事件就传给ViewGroup消费了
        isDispatch = super.disPatchTouchEvent(motionEvent);
    }
    return isDispatch;
}

到这里相信大家应该能够明白,事件分发的U型图的含义了,以上就是down时间的分发原理;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值