Android源码事件传递流程剖析

来源:互联网 发布:淘宝炫舞摘花怎么样 编辑:程序博客网 时间:2024/06/06 00:38

    这篇文章基于Android4,.2的源码分析得出,水平有限,如有错误请及时指出,谢谢!

    这片文章讲解的事件传递的起源从dispatchTouchEvent(event)开始,根据事件的处理流程逐渐展开,直至事件被可预料的处理掉结束。

    先贴一张个人总结的事件传递的流程图,如果可以将这张图清楚的理解,下面的文章就可以不用看了,因为这篇文章的主要内容也就是围绕这幅图展开。


一、ViewGroup中的事件处理

    当事件传递到dispatchTouchEvent方法中,由于ViewGroup是View的子类,所以如果当前接收到事件的是ViewGroup将首先触发该类的dispatchTouchEvent方法(不过android界面最外层肯定是个ViewGroup所以肯定会先触发这个事件)。

    该方法会先判断该事件是否可被中断(默认为是),然后便触发ViewGroup才有的onInterceptTouchEvent方法,该方法用于ViewGroup截取事件处理,假如该方法返回true则代表已经将事件截取。

if (!disallowIntercept) {        intercepted = onInterceptTouchEvent(ev);        ev.setAction(action); // restore action in case it was changed} else {        intercepted = false;}

    在你自定义的视图中,如果想要截取事件,就可以继承onInterceptTouchEvent方法。而在ViewGroup的源码中并没有做截取动作,直接返回了false。

public boolean onInterceptTouchEvent(MotionEvent ev) {        return false;}
    在确定事件没有被截取之后,ViewGroup中会判断自己是否有子视图符合接收该事件的条件,如果有的话,则直接将事件分发给该子视图,并返回true代表在这层事件已经被分发出去。否则的话则将该视图视为普通View继续处理事件(详细参见后面的View处理事件部分)。
if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {continue;}newTouchTarget = getTouchTarget(child);if (newTouchTarget != null) {// Child is already receiving touch within its bounds.// Give it the new pointer in addition to the ones it is handling.newTouchTarget.pointerIdBits |= idBitsToAssign;break;}resetCancelNextUpFlag(child);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();newTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;break;}
二、View中的事件处理
    不管上面的那种情况最终都会通过dispatchTransformedTouchEvent方法最后调用到View中的dispatchTouchEvent方法。该方法首先会调用到的是mOnTouchListener.onTouch方法,其中mOnTouchListener是我们在使用view的setOnTouchListener方法时注册进去的监听。如果我们在注册进去监听的onTouch方法中返回true则代表该事件已经被处理,事件将不会在继续传递。

ListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {return true;}if (onTouchEvent(event)) {return true;}

    而如果没有设置监听或返回为false的话,该事件将会被传递到onTouchEvent方法。在View类的onTouchEvent方法中,由于是最简单的视图,只处理了和click相关的事件。从注释可以看出包括click和longClick事件。

if (mPerformClick == null) {mPerformClick = new PerformClick();}if (!post(mPerformClick)) {performClick();}


    在performClick中则调用到我们为view设置的clicklistener监听。

ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);li.mOnClickListener.onClick(this);return true;}


    在这里我没有看到关于longClick事件的处理,暂时还没有找到,下次找到了再继续更新。

三、总结

  • 首先接收到事件的视图是最外层的视图,然后再往子视图上传递;
  • 事件的传递是一个递归过程;
  • 在上述每个环节都可以通过返回true的方法,结束事件的传递;
  • 自定义视图的事件传递过程决定于其对应继承的方法,但应该遵守上述的规则。





0 0
原创粉丝点击