您的位置:首页 > 移动开发 > Android开发

Android源码事件传递流程剖析

2014-04-07 17:04 239 查看
    这篇文章基于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的方法,结束事件的传递;
自定义视图的事件传递过程决定于其对应继承的方法,但应该遵守上述的规则。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: