(该文所引用的源码代码出自Android 5.1版本)
一直想搞清楚Android 事件分发的各个函数调用的关系,前几天突然想起Debug时会显示出线程函数调用的关系,于是,写了一个很简单的程序,在界面上显示一个Button,并且为这个Button注册onTouch事件,在里面打个断点:
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
然后一运行程序,点击一下按钮,果然,函数之间的调用关系出来了:
从上面的图可以看到事件分发时各个函数调用的先后顺序,为了看的更加清楚,画了一个简单的流程图,蓝色背景的表示我们可以通过重写父类的方法控制和改变的方法:
当点击事件时,native层调用Java层InputEventReceiver中的dispatchInputEvent方法,名字很好理解,是用来分发输入事件的:
// Called from native code.
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
这个方法接着调用WindowInputEventReceiver的onInputEvent方法,接着还有一系列的调用,最后,会调用ViewRootImpl中的processPointerEvent方法:
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
boolean handled = mView.dispatchPointerEvent(event);
if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
mUnbufferedInputDispatch = true;
if (mConsumeBatchedInputScheduled) {
scheduleConsumeBatchedInputImmediately();
}
}
return handled ? FINISH_HANDLED : FORWARD;
}
processPointerEvent方法第4行,调用了mView.dispatchPointerEvent(event),找到最开始调用View方法的地方,那这个mView究竟是何方神圣?通过一系列代码追踪,mView就是每个Activity实例里面的mDecor,也就是DecorView的一个实例。通过查看布局层次,可以发现DecorView是最顶层的View,所以可以确定,Android事件的分发是从DecorView开始的。
mView.dispatchPointerEvent(event)相当于调用View的dispatchPointerEvent方法:
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
在方法里面会调用dispatchTouchEvent(event),因为DecorView重写了dispatchTouchEvent(event)方法,所以会调用DecorView的dispatchTouchEvent(event):
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
从方法里面可以看到,如果cb不为空,并且窗口没有销毁时调用cb.dispatchTouchEvent(ev),而Callback是Window类的接口:
/** * API from a Window back to its caller. This allows the client to * intercept key dispatching, panels and menus, etc. */
public interface Callback {
public boolean dispatchKeyEvent(KeyEvent event);
public boolean dispatchKeyShortcutEvent(KeyEvent event);
public boolean dispatchTouchEvent(MotionEvent event);
public boolean dispatchTrackballEvent(MotionEvent event);
......
}
所以,会调用Activity中的dispatchTouchEvent方法:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
在里面调用DecorView的superDispatchTouchEvent方法:
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
接着调用super.dispatchTouchEvent(event),也就是调用ViewGroup的dispatchTouchEvent(event),开始一级一级向下传递。
ViewGroup事件分发主要通过dispatchTouchEvent方法完成:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it,
// start
// normal event dispatch. Maybe a descendant is what will handle the
// click.
if (ev.isTargetAccessibilityFocus()
&& isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch
// gesture.
// The framework may have dropped the up or cancel event for the
// previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
// 动作为手指按下的时候
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// requestDisallowInterceptTouchEvent调用,不让父View拦截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 调用onInterceptTouchEvent,判断是否拦截事件
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;
}
// If intercepted, start normal event dispatch. Also if there is
// already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果不拦截事件
if (!canceled && !intercepted) {
// If the event is targeting accessiiblity focus we give it to
// the
// view that has accessibility focus and if it does not handle
// it
// we clear the flag and dispatch the event to all children as
// usual.
// We are looking up the accessibility focused host to avoid
// keeping
// state since these events are very rare.
View childWithAccessibilityFocus = ev
.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus()
: null;
// 判断触摸事件的种类
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for
// down
final int idBitsToAssign = split ? 1 << ev
.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in
// case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
// 如果有子View
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
// 所有的子View从上往下排序,或是是绘制的顺序
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
// 遍历所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder ? getChildDrawingOrder(
childrenCount, i) : i;
// 获取子View
final View child = (preorderedList == null) ? children[childIndex]
: preorderedList.get(childIndex);
// If there is a view that has accessibility focus
// we want it
// to get the event first and if not handled we will
// perform a
// normal dispatch. We may do a double iteration but
// this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
// 判断子View是否VISIBLE,点击坐标是否在View的范围内
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y,
child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
// 得到TouchTarget
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);
//如果触摸位置在child的范围内,进行事件分发
if (dispatchTransformedTouchEvent(ev, false, child,
idBitsToAssign)) {
// Child wants to receive touch within its
// bounds.
// 如果子View消费了Touch事件
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list,
// find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child,
idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event,
// so clear
// the flag and do a normal dispatch to all
// children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null)
preorderedList.clear();
}
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added
// target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
// 只有调用addTouchTarget,mFirstTouchTarget才可能不为空。要调用addTouchTarget
// 必须有子View消费了Action.Down事件,否则super.dispatchTouchEvent(event),ViewGroup把自己当做View
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//View消费了Action.Down事件,MOVE,UP等事件开始执行
// Dispatch to touch targets, excluding the new touch target if
// we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget
&& target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
if (canceled || actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
代码比较长,只分析比较关键的代码34行有一个判断:
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null)
只有动作为ACTION_DOWN或者mFirstTouchTarget != null时才会往下走。
第37行:
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
这里会得到一个boolean变量,这个变量主要用处是控制ViewGroup在分发事件时是否调用onInterceptTouchEvent方法:
if (!disallowIntercept) {
// 调用onInterceptTouchEvent,判断是否拦截事件
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was
// changed
}
只有当disallowIntercept的值为false时,才会调用onInterceptTouchEvent方法,而disallowIntercept 的取值取决于我们调用requestDisallowInterceptTouchEvent这个方法:
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
}
if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
当传入参数为true时,disallowIntercept的值为true,不会在调用onInterceptTouchEvent方法,这个方法常常是子View调用,不让父View拦截事件。
40行intercepted变量控制父View是否拦截事件,它的取值除了受到上面disallowIntercept的控制外,由onInterceptTouchEvent方法的返回值控制:
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
默认是返回false,是不拦截子View的事件。
99行,判断该ViewGroup是否包含子View,如果包含子View,遍历子View,132行调用isTransformedTouchPointInView判断触摸的位置是否在View的范围内:
protected boolean isTransformedTouchPointInView(float x, float y,
View child, PointF outLocalPoint) {
final float[] point = getTempPoint();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
// 判断点击的坐标是否在View的范围内
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
如果触摸在子View的范围类,则通过调用dispatchTransformedTouchEvent进行子View的事件分发:
private boolean dispatchTransformedTouchEvent(MotionEvent event,
boolean cancel, View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any
// transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it
// looks like we
// might produce a motion event with no pointers in it, then drop the
// event.
// 不是手指触发的点击事件,直接返回
if (newPointerIdBits == 0) {
return false;
}
// If the number of pointers is the same and we don't need to perform
// any fancy
// irreversible transformations, then we can reuse the motion event for
// this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
// 子View为空,交给ViewGroup处理
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
// 调用子View的事件分发
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
// 从池子里面获取一个MotionEvent
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
// 子View为空,ViewGroup调用View的dispatchTouchEvent,接着调用自己的onTouchEvent
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (!child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
// 调用子View的事件分发
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
在这个方法里面,又会调用子View的dispatchTouchEvent方法,一直递归下去,这时会分两种情况:
第一种:如果外层的View传递的过程中,有View消费了事件,则传递停止,继续往下执行。
第二种:已经传递到最外层的View,但是最外层的View仍然不消费事件,则沿着事件传递的反方向继续传递,交给该View的父View消费事件,继续递归往里面传递,直到消费事件。
203行有个判断:
if (mFirstTouchTarget == null)
只有调用addTouchTarget,mFirstTouchTarget才可能不为空。要调用addTouchTarget 必须有子View消费了Action.Down事件,否则调用super.dispatchTouchEvent(event),也就是View的分发方法。
如果有子View消费了Action.Down事件,那么会走到else里面,一般只有MOVE,UP动作会走到这里,222行又是调用dispatchTransformedTouchEvent进行事件分发。
ViewGroup进行事件分发时,一般最好分发到View,调用View的dispatchTouchEvent,View的dispatchTouchEvent方法相对比较简单:
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
//1、判断是否设置OnTouchListener
//2、判断View的状态是否是ENABLED
//3、判断View onTouch 是返回true,是否消费事件
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//如果View的 onTouch没有消费事件,执行onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
30行的判断,满足三个条件,将不会调用View的onTouchEvent,如果在Activity里面,我们为某个状态为ENABLED的View设置了mOnTouchListener事件,并且onTouch方法返回true,那么相当于消费了事件,36行onTouchEvent不会执行,本次View的事件分发就此结束。
如果这三个条件有一个不满足,那么接着会调用onTouchEvent方法,进行事件的处理:
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
//View 的状态是DISABLED,如果是可点击或者可以长按,可以消费事件
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//如果View是可以点击的
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
//移除长按的回调函数
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//调用View 的OnClickListener
if (!post(mPerformClick)) {
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
//根系按压状态为false
if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
if (performButtonActionOnTouchDown(event)) {
break;
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
//判断View是否在可以滚动的父View里面。
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
//TAP_TIMEOUT 判断用户是否想要滚动,而不是长按,相当于比不滚动的View多了500毫秒的时间
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
//处理长按事件
checkForLongClick(0);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
break;
case MotionEvent.ACTION_MOVE:
drawableHotspotChanged(x, y);
// Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
setPressed(false);
}
}
break;
}
return true;
}
return false;
}
第7行先判断View的状态是否是DISABLED,如果是的话,会直接返回。第23行,判断View是否是可点击或者是可以长按的,如果满足条件,就进入下面Action判断,一最先执行的是ACTION_DOWN事件,106行通过调用checkForLongClick,处理长按事件:
private void checkForLongClick(int delayOffset) {
if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
}
mPendingCheckForLongPress.rememberWindowAttachCount();
postDelayed(mPendingCheckForLongPress,
ViewConfiguration.getLongPressTimeout() - delayOffset);
}
}
通过post一个延迟为500毫秒的Runnable对象,等到Runnable执行的时候,检查View是否是按下状态,如果是的话,就当做一次长按事件,调用Activity里面监听的长按事件。
一般DOWN事件返回后,接下来是MOVE事件,在处理MOVE事件时,主要是移除长按长按的回调。MOVE事件结束后,就是UP事件处理Activity设置的点击事件,最终会调用:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
到这里,事件分发的理论分析完了。