点击屏幕Android事件是如何传递,先上图然后再源码解读
源码解读
当用户触摸屏幕首先由当前Activity来分发
public class TouchEventActivity extends AppCompatActivity {
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_touch_event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
}
public class Activity{
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//getWindow() 获取的是PhoneWindow PhoneWindow.superDispatchTouchEvent
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
}
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private DecorView mDecor;
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
}
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
public boolean superDispatchTouchEvent(MotionEvent event) {
// DecorView 为FrameLayout, FrameLayout的父类为ViewGroup所以最后分发代码是城ViewGroup的dispatchTouchEvent
return super.dispatchTouchEvent(event);
}
}
ViewGroup.dispatchTouchEvent源码解读(事件是如何分发的)
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
// 相关成员变量
private TouchTarget mFirstTouchTarget;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 每一次ACTION_DOWN事件都会重置一此参数,
// 所以requestDisallowInterceptTouchEvent这个方法的返回值在ACTION_DOWN事件里是无效的
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
// 如果不是ACTION_DOWN事件-->只有在ACTION_DOWN 时找到了事件消费的人既mFirstTouchTarget != null 则会走进这块代码
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
// 这行代码返回值和requestDisallowInterceptTouchEvent和调用这个方法有关,相当于一把拦截的上方宝剑,
// 但是如果是ACTION_DOWN 事件disallowIntercept 这个一定是返回false
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 事件拦截 拦截intercepted 则为true,这个影响下面if (!canceled && !intercepted) 这个的判断
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 如果onInterceptTouchEvent返回false,则不拦截事件,则进入到里面
if (!canceled && !intercepted) {
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();
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 获取到了view的集合,一般情况是根据xml的先后顺序来的,但是如果xml里有translationZ属性就不一样
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 (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);
// dispatchTransformedTouchEvent这个方法的返回值是判断是否继续for循环还是直接跳出循环
// dispatchTransformedTouchEvent内部会处理事件,
// 如果这个child为ViewGropu的话,则递归Viewgroup的dispatchTouchEvent
// 如果child为View则直接处理事件了
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
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;
}
}
if (preorderedList != null) preorderedList.clear();
}
}
}
// mFirstTouchTarget == null这个为null ,代表着没人消费或者事件被拦截了。则直接交给自己的父类处理事件
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
// 这个whild如果是单点触控是循环一次,如果是多点就是多次
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
// 如果intercepted为true时这个cancelChild就是true
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
// 如果有人消费事件alreadyDispatchedToNewTouchTarget这个会设置为true,
// target == newTouchTarget这个也是相等的所以直接handled = true;
if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
handled = true;
}
// 如果onInterceptTouchEvent返回true则为true 如果是MOVE事件,mFirstTouchTarget == null时这个也返回true
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
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;
}
}
事件是继续向下分发还是直接处理由下面这个方法处理
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
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();
// 如果是cancel 则会将这个event的Action直接设置为ACTION_CANCEL
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
// 传入的child为null,则是直接处理事件
handled = super.dispatchTouchEvent(event);
} else {
// 传入的child不为null,则是继续分发事件,如果child为View的话相当于是处理事件了
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) {
// 传入的child为null,则是直接处理事件
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
// 传入的child不为null,则是继续分发事件,如果child为View的话相当于是处理事件了
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
// 传入的child为null,则是直接处理事件
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());
}
// 传入的child不为null,则是继续分发事件,如果child为View的话相当于是处理事件了
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
}
事件处理(View.dispatchTouchEvent)
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
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;
// 如果设置了onTouchListener的话就需要会走到onTouch方法,判断onTouch返回值是true还是false
// 如果返回true则result = true
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// 如果onTouch把事件消费了,则返回为true,则onTouchEvent这个方法就不走了 反之会走onTouchEvent方法
// 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;
}
}
分析真正处理事件的方法
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
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)) {
//处理点击事件回调
performClickInternal();
}
}
}
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) {
//检查是否是长按事件,这里会发送一个Msg
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);
// 检查是否是长按事件,这里会发送一个Msg
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;
}
}
再次分析一些重要的代码对其它事件的影响
// 这块业务ACTION_DOWN 是一定会进来
// 但是final boolean disallowIntercept这个变量ACTION_DOWN 一直是返回fase,上面已经分析过。
// mFirstTouchTarget 这个变量,如果已经有View消费了事件则这个值一定不为null.
// 一般情况事件都是有View消费的,所以其它事件基本都会进入到这里
// 所以disallowIntercept 这个变量影响的就是除ACTION_DOWN 外的其它事件。
// intercepted 这个变量为true or false 则是由变量disallowIntercept 和方法onInterceptTouchEvent决定的。
//disallowIntercept这个变量为true or false 又是由requestDisallowInterceptTouchEvent方法决定。
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
// 事件拦截都是在MOVE里来处理的,假如当前类的调用了requestDisallowInterceptTouchEvent方法传入true,
// 则disallowIntercept为true; intercepted这时为false
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;
}
示例:假如一个TestPageView里面有2个Fragment, 第一个Fragment里面是一个TestRecyclerView
public class TestPageView extends ViewPager {
public TestPageView(@NonNull Context context) {
super(context);
}
public TestPageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
super.onInterceptTouchEvent(ev);
return false;
}
return true;
}
}
public class TestRecyclerView extends RecyclerView {
private int mLastY, mLastX;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getX();
int moveY = (int) ev.getY();
if (Math.abs(moveX - mLastX) > Math.abs(moveY - mLastY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(ev);
}
}
TestRecyclerView
在第一次ACTION_DOWN事件的时候调用了getParent().requestDisallowInterceptTouchEvent(true);
接下来开始分析上述情况的MOVE事件。
分析第一次MOVE事件进来
// mFirstTouchTarget 这个肯定不为null了 进入到里面
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
// ViewPage不调用requestDisallowInterceptTouchEvent此方法
// 在TestRecyclerView的ACTION_DOWN调用了getParent().requestDisallowInterceptTouchEvent(true)
// 所以disallowIntercept 为true;
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 {
intercepted = true;
}
//到这里 intercepted为false mFirstTouchTarget 也不为null
if (mFirstTouchTarget == null) { //这里不走
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
// 由于是单点所以target.next为null TouchTarget next = null;
final TouchTarget next = target.next;
// alreadyDispatchedToNewTouchTarget 这个被重新附为false
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { // 所以这里不走
handled = true;
} else {
// intercepted为false --> cancelChild 为false
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
// 这个方法上面分析过了这里就不多做分析 target.child 这个传入的是就是ReceycleView
// ReceycleView是ViewGroup,它本身是不处理dispatchTouchEvent事件的,
// 所以这事件又会继续向ReceycleView子view分发,如此循环
// 所以这个方法返回的是true handled 则也为true
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
// cancelChild这里是为false,
if (cancelChild) { // 不进入里面
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
// 由于分析的是单点 所以 target 已经为null了跳出循环
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);
}
return handled; //最后就return了handled = true;
分析第二次MOVE事件进来
假如第二次MOVE事件满足下面逻辑
if (Math.abs(moveX - mLastX) > Math.abs(moveY - mLastY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
再来分析上面代码
// mFirstTouchTarget 这个肯定不为null了 进入到里面
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
// 由于ViewPage子View(RecycleView)调用了getParent().requestDisallowInterceptTouchEvent(false); 传入了false
// 所以disallowIntercept 为false
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) { // 进入里面
// TestPageView里的onInterceptTouchEvent在除ACTION_DOWN都返回的是true
// 所以 intercepted 为true
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
//到这里 intercepted为true, mFirstTouchTarget 也不为null
if (mFirstTouchTarget == null) { //这里不走
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
// 由于是单点所以target.next为null TouchTarget next = null;
final TouchTarget next = target.next;
// alreadyDispatchedToNewTouchTarget 这个被重新附为false
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { // 所以这里不走
handled = true;
} else {
// intercepted为true--> cancelChild此时为为true了
final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
// cancelChild 为true
// 这个方法上面分析过了这里就不多做分析 target.child 这个传入的是就是ReceycleView
// ReceycleView是ViewGroup,它本身是不处理dispatchTouchEvent事件的,
// 所以这事件又会继续向ReceycleView子view分发,如此循环
// 所以这个方法返回的是true handled 则也为true
// cancelChild为true时,dispatchTransformedTouchEvent方法内部会调用event.setAction(MotionEvent.ACTION_CANCEL);
if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
handled = true;
}
// cancelChild这里是为true,
if (cancelChild) { // 进入里面
// predecessor 为null
if (predecessor == null) { // 进入里面.
// next 在这里为null 所以mFirstTouchTarget 为null
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
// 由于分析的是单点 所以 target 已经为null了跳出循环
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);
}
return handled; //最后就return了handled = true;
接下来的MOVE事件
在上面MOVE事件中mFirstTouchTarget 此时又被附值为null了
则直接走下面代码
if (mFirstTouchTarget == null) {
// 事件的处理权又到了父类这里
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
所以上面示例,如果在RecycleView上下滑动的时候再左右滑动的过程中事件先是由RecycleView来处理,当在左右滑动的时候又会将事件处理权交给ViewPage, 如果再上下滑动,事件就不可能传到RecycleView了
事件处理的时候可以从子 到父,父到子是不可能的,