android事件分发
1). android对事件分发的顺序为:Activity--->PhoneWindow--->DecorView--->yourView;
2). android控件对事件处理的优先级:onTouch--->onTouchEvent--->onClick
Android既然可以对事件进行拦截,肯定有某个方法对事件进行的传递或者分发,完成事件分发功能是由Activity的dispatchTouchEvent(MotionEvent ev)l来负责:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//该方法为空方法,有用户交互的时候的回调,注意只在ACTION_DOWN中回调
onUserInteraction();
} //交由phoneWindow来处理
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
} 12 //否则,调用Activity的onTouchEvent方法
return onTouchEvent(ev);
}
上述分发事件的方法dispatchTouchEvent, 先把事件分发给Window, 通过之前写的博客知道这个Window其实就是PhoneWindow,那就看看PhoneWindow方法都做了些什么:
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
很简单把此次事件直接很光棍的传给DecorView,这个View是所有视图的根视图,Activity界面中你能见到的各个View都是DecorView的子View。到此为止事件已经分发到View上面,View获取到事件后有两个选择:处理和不处理该事件,如果处理该事件那事件就不会继续向其子View分发下去;否则就继续分发下去交给子View对该事件做同样的判断,其实就是个递归的过程。下面是DecorView里面的调用方法
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
1)ViewGroup永远不会对拦截,因为他的onInterceptTouchEvent(MotionEvent ev)始终返回的是false!这样DecorView对到来的事件MotionEvent就只有分发到子View并由子View进行拦截和处理此事件了.
2)View包括直接继承于View的子类因为其父类View没有onInterceptTouchEvent方法,所以没法对事件进行拦截,如果这种View获取到了事件,那么就会执行onTouchEvent方法(当然这也是有条件的,这个前提条件在对下面onTouch方法作用的时候会有说明)。
A) View类对事件的处理:
比如Button的直接父类TextVew的父类为View,那么当Button获取事件的时候其执行分发和处理的时候调用dispatchTouchEvent,就先分析一下View的这个方法都做了什么以供博客后面的篇幅用:
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)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//如果这里返回true的话,那么也表明此次事件被处理了
result = true;
} 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;
}
通过上面可以知道onTouchListener的onTouch方法优先于onTouchEvent执行,这个onTouch是否能实行取决于你的View有没有调用setOnTouchListener方法设置OnTouchListener。如果onTouch方法返回的true,那么View的dispatchTouchEvent方法就返回true而结束执行,onTouchEvent方法就不会得到执行;因为onClick方法通过下面的分析也知道是在onTouchEvent方法中执行的,所以此时onClick方法也不会执行了。
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction(); final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return clickable;
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
} if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
if ((viewFlags & TOOLTIP) == TOOLTIP) {
handleTooltipUp();
}
if (!clickable) {
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
break;
}
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 && !mIgnoreNextUpEvent) {
// 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();
}
if (!post(mPerformClick)) {
performClick();
72 }
}
} if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
} if (prepressed) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
} removeTapCallback();
}
mIgnoreNextUpEvent = false;
break; case MotionEvent.ACTION_DOWN:
if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
}
mHasPerformedLongPress = false; if (!clickable) {
checkForLongClick(0, x, y);
break;
} 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.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(0, x, y);
}
break; case MotionEvent.ACTION_CANCEL:
if (clickable) {
setPressed(false);
}
removeTapCallback();
removeLongPressCallback();
mInContextButtonPress = false;
mHasPerformedLongPress = false;
mIgnoreNextUpEvent = false;
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
break; case MotionEvent.ACTION_MOVE:
if (clickable) {
drawableHotspotChanged(x, y);
} // Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
// Remove any future long press/tap checks
removeTapCallback();
removeLongPressCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
}
break;
} return true;
} return false;
}
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); notifyEnterOrExitForAutoFillIfNeeded(true); return result;
}
以上简单的说下onTouch和onTouchEvent和onClick的执行顺序以及onTouch的返回值对onTouchEvent和onClick的影响。
到此为止,关于View对事件的分发和处理算是简单的分析完毕,也可以得到一个结论:如果View类的的dispatchTouchEvent返回true的话,就表明有某个View对该起事件负责(进行处理)。
通过View类的onTouchEvent方法我们也很容易得到如下两个结论,该结论主要有上方代码的如下语句得来的
if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE))
这句代码是说如果这个view是可点击的(clickable=true或者longClickable=true),那么这样的View最终都会消耗当前事件而让View的dispatchTouchEvent返回true,这样的View就是下面将要说到的消耗事件的target对象!否则,即如果一个View既不是clickable也不是longClickable的话,那么这个View不会消耗该事件。
B)ViewGroup类对事件分发的处理:
下面继续分析在ViewGroup的dispatchTouchEvent方法,该方法中开始有这么一段:
@Override
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) {
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;
}
43 //如果不允许当前View拦截该事件或者没有拦截该事件,就让当前View的子类去分发和拦截,递归过程
// 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;
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.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, 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;
} if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
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();
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.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// 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,
180 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;
}
- //如果不允许当前View拦截该事件或者没有拦截该事件
- //就让当前View的子类去分发和拦截,递归过程
- if (disallowIntercept || !onInterceptTouchEvent(ev)) {
- // reset this event's action (just to protect ourselves)
- ev.setAction(MotionEvent.ACTION_DOWN);
- // We know we want to dispatch the event down, find a child
- // who can handle it, start with the front-most child.
- final int scrolledXInt = (int) scrolledXFloat;
- final int scrolledYInt = (int) scrolledYFloat;
- final View[] children = mChildren;
- final int count = mChildrenCount;
- //遍历ViewGroup的子View,在此为遍历DecorView的子View
- for (int i = count - 1; i >= 0; i--) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
- || child.getAnimation() != null) {
- child.getHitRect(frame);
- if (frame.contains(scrolledXInt, scrolledYInt)) {
- .........
- //交给子View的dispatchTouchEvent进行对此事件的分发拦截。
- //如果返回true说明child处理了此事件,递归调用
- if (child.dispatchTouchEvent(ev)) {
- // Event handled, we have a target now.
- //说明已经找到处理此次事件的子View,用mMotionTarget记录
- mMotionTarget = child;
- //找到目标,此次down事件结束。
- return true;
- }
- // The event didn't get handled, try the next view.
- // Don't reset the event's location, it's not
- // necessary here.
- }
- }
- }
- }
- }
上诉代码的for循环很详细的说明了View对事件进行分发的过程,当然for循环之所以能得以执行使用两个前提条件的:
1)事件为ACTION_DOWN事件,在Down事件中才去进行分拦截发事件并寻找消耗事件的target View,!
2)disallowIntercept ,这个属性可通过requestDisallowInterceptTouchEvent(boolean disallowIntercept )来设置,通过观察期具体实现可以知道该方法的作用就是子View干预父View的事件分发过程,当然对于ACTION_DOWN事件子类是不能干预父类的,因为if条件为(disallowIntercept ||!onInterceptTouchEvent(ev))为或操作;或者当前的View没有拦截成功该事件。如果disallowIntercept 为true,那么就说明当前ViewGroup的某个子View不允许其父View对当前事件进行拦截,反之就是某个子View允许其父View对当前事件进行拦截,当然这并不等于父View一定能拦截成功当前事件,如果父View此时拦截不成功(即父View的onInterceptTouchEvent返回false),那么仍然交给子view进行分发拦截逻辑
:
- @Override
- public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- //省略部分代码
- // Pass it up to our parent
- if (mParent != null) {
- //当前View干预其父View对后续事件的分发
- mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
- }
如果当前的ViewGroup(为了方便再次称之为ParentView)允许对此次事件进行拦截或者ParentView没有对此事件拦截成功(ParentView的onInterceptTouchEvent返回false)简而言之就是如果ParentView不拦截这处理该事件,就把该事件分发到ParentView的若干子类中去,循环遍历它的子类,来寻找是否有某个子类对处理该事件。可以参照上面的伪代码和流程图来理解之。
如果找到了这样的View,就对该View用一个变量mMotionTarget进行标识。如果在当前的ParentView的子View中没有找到处理该事件的子View会怎么办呢?在ViewGroup里面的dispatchTouchEvent在上面的for循环之后有如下代码:
- //如果没有找到处理事件的View
- if (target == null) {
- // We don't have a target, this means we're handling the
- // event as a regular view.
- ev.setLocation(xf, yf);
- if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
- ev.setAction(MotionEvent.ACTION_CANCEL);
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- }
- //ViewGroup调用View的事件分发方法,之所以能进入当前的View是因为当前的View的父View没有拦截成功此事件。
- return super.dispatchTouchEvent(ev);
- }
从上面的代码可以看出:如果在ParentView的子类中没有找到能处理问题的那个view,就调用parentView的父View的dispatchTouchEvent方法。我们应该知道之所以parentView能分发和拦截事件,前提是它的父类本来没有拦截事件的能力或如本身拦截事件的方法返回了false,所以沿着view树最终会调用的View类的dispatchTouchEvent,那么又回归到本篇博客的A)View类对事件的处理那一部分了。
注意前面讲的for循环查找的重大前提是:在down事件中,且我们要明白在手指接触屏幕到手指离开屏幕会产生一系列事件,一个down(ACTION_DOWN)事件,数个move(ACTION_MOVE)事件,和一个 UP事件。寻找到目标事件之后,之后的一些列事件都交给这个target消耗,比如move事件等。当然我们是可以通过让target调用requestDisallowInterceptTouchEvent方法来干预父类关于事件分发过程。或者在在适当的情况下让target父View的onInterceptEvent返回true或者false,来解决滑动问题事件的冲突问题:
- if (!disallowIntercept && onInterceptTouchEvent(ev)) {
- final float xc = scrolledXFloat - (float) target.mLeft;
- final float yc = scrolledYFloat - (float) target.mTop;
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- ev.setAction(MotionEvent.ACTION_CANCEL);
- ev.setLocation(xc, yc);
- if (!target.dispatchTouchEvent(ev)) {
- // target didn't handle ACTION_CANCEL. not much we can do
- // but they should have.
- }
- // clear the target
- mMotionTarget = null;
- // Don't dispatch this event to our own view, because we already
- // saw it when intercepting; we just want to give the following
- // event to the normal onTouchEvent().
- return true;
- }
最后某个View(target)如果开始处理事件,在手指离开屏幕之前的什么move事件啦,up事件啦都会交给这个View(target)来处理,因为在ViewGroup的diapatchTouchEvent代码的最后会执行:
- //把事件交给目标视图来处理
- return target.dispatchTouchEvent(ev);
并且本View的onInterceptTouchEvent是不会调用了。也就是说如果有一个view处理该事件,那么down之后的一系列move事件和up事件都自动交给该view处理,因为该view已经是targetView 了,所以不会对后续事件序列或者事件集合进行拦截操作,只会调用dispatchTouchEvent和onTunchEvent来处理该事件!而onInterceptTouchEvent事件不会调用。简单的举个例子,如图:
假设上图中由D进行事件的处理,也就是说D的onInterceptTouchEvent和onTouchEvent均返回true,D的父View A ,B ,C的这两个方法都返回false,那么在这个布局中D就是上面所说的 target.在首次的Down事件中会执行查找target的操作:(注:下图中分发事件指的是执行了dispatchTouchEvent,拦截事件指的是onInterceptTouchEvent,消耗事件为onTouchEvent方法)
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- Log.e(null, "B--分发事件");
- return super.dispatchTouchEvent(ev);
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- Log.e(null, "B--拦截事件");
- return super.onInterceptTouchEvent(ev);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- Log.e(null, "B--处理事件");
- return super.onTouchEvent(event);
- }
注意D此时的打印,D为此事件的target View,拦截和处理了该次down事件,那么如果此时手指继续移动的话将会打印如下的Log:
注意上图是手指滑动后打印的log,可以发现D只负责分发和处理该事件,而没有像down事件那样进行拦截,所以上面的log打印可以清晰的说明上面的结论:
1)targetView只会对一个事件序列拦截一次,即只拦截down事件或者说由down事件负责查找targetView
2)一旦targetView被找到,down事件之后的一些列事件都由target View负责消耗,而target并不对后续事件进行再次拦截
3)当然在down事件之后的后续事件还是会先由父View进行分发拦截,也即是说文章开头所说的事件序列中把每一个事件单独来看的话,都会由父View来进行拦截和分发的,只不过到后续事件到传到target的时候直接进行处理而少了拦截的过程而已,因为在父类查找target的时候已经拦截过一次,这点很重要,也是解决滑动冲突的关键点,比如滑动的时候根据合适的时机来判断是否让父View进行事件拦截和处理。只不过省下了对targetView的寻找,因为在down事件中已经寻找到了target并有mMotionTarget变量进行了标识,(通过上面的对ViewGroup的dispatchTouchEvent源码的解析就可以表明出来)
android事件分发的更多相关文章
- Android事件分发机制浅谈(一)
---恢复内容开始--- 一.是什么 我们首先要了解什么是事件分发,通俗的讲就是,当一个触摸事件发生的时候,从一个窗口到一个视图,再到一个视图,直至被消费的过程. 二.做什么 在深入学习android ...
- 通俗理解Android事件分发与消费机制
深入:Android Touch事件传递机制全面解析(从WMS到View树) 通俗理解Android事件分发与消费机制 说起Android滑动冲突,是个很常见的场景,比如SliddingMenu与Li ...
- Android事件分发机制(下)
这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...
- Android事件分发机制(上)
Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...
- android事件分发机制
android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...
- [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...
- android事件分发介绍
Android事件分发 事件分发3个步骤 dispatchTouchEvent(event)派发 onInterceptTouchEvent(event)拦截 onTouchEvent(eve ...
- Android事件分发理解
Android事件分发机制是个难点和重点,结合下各家,写点自己的理解.. 首先抛出一个小问题,写一个button的点击事件 button.setOnClickListener(new OnClickL ...
- Android事件分发机制源码分析
Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...
- 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...
随机推荐
- vs2005+WinCE模拟器+ActiveSync调试WinCE程序
来源:http://www.cnblogs.com/xjimmyshcn/archive/2011/07/19/2111087.html 一.WinCE 模拟器通过ActiveSync 6.1(即Wi ...
- erlang和ruby互相调用
erlang调用ruby https://github.com/mojombo/erlectricity ruby调用erlang https://github.com/davebryson/rint ...
- C语言课程设计——电影院订票系统
1. 课题简介 大家都爱看电影,现请参考一个熟悉电影票预订系统,实现C语言版的订票系统.了解订票如何实现的.系统主要有2类用户:管理员用户和顾客用户. 管理员用户登录系统后,实现电影放映厅信息管理和电 ...
- java之扫描包里面的class文件
一.class作为,编译过后的产物,在很多时候,我们需要通过反射去执行class的具体方法.但是扫描class就是一个很大的问题了. 二.所以我这里写了一个简单的class文件扫描方式. 三.主要是利 ...
- php error_log记录日志的使用方法和配置 (日志目录一定要手动创建)
对于PHP开发者来 说,一旦某个产品投入使用,应该立即将 display_errors选项关闭,以免因为这些错误所透露的路径.数据库连接.数据表等信息而遭到黑客攻击.但是,任何一个产品在投入使用后,都 ...
- 双系统(Windows+Ubuntu)重装Ubuntu后,修复引导
重装Ubuntu后,重启机器,Windows系统消失了.进入BIOS修改启动顺序,Windows系统恢复了,但是,刚装好的Ubuntu没了,一开机,电脑自动进入Windows. 解决办法: 用装系统的 ...
- C++中如何强制inline函数(MSVC, GCC)
#ifdef _MSC_VER_ // for MSVC #define forceinline __forceinline #elif defined __GNUC__ // for gcc on ...
- MongoDB 生态 – 可视化管理工具
工欲善其事,必先利其器,我们在使用数据库时,通常需要各种工具的支持来提高效率:很多新用户在刚接触 MongoDB 时,遇到的问题是『不知道有哪些现成的工具可以使用』,本系列文章将主要介绍 MongoD ...
- 传统项目利用Hystrix实现热点接口的服务隔离
这段时间接了个需求,需要在我目前负责的数据系统上加个接口,主要是实现用户行为的记录.前端对接的项目主要有公司的PC,WAP,WEIXIN,APP等,每个端大概有两台左右的负载.因为目前我的这个项目主要 ...
- SQL Server修改表结构后批量更新所有视图
最近修改了数据库表结构,数据同步的时候出了问题,发现很多数据明明已经修改,但是通过视图筛选出来的还是原来的数据,所以怀疑应该是视图缓存了数据,在园子里找到下面的博文,在这里做个记录备忘. 原文链接:h ...