前言:

Android事件分发机制让我头疼了很久,很多概念容易混淆,在简书上看到一篇绝赞的文章,在阅读view源码的帮助下,终于可以说是大致搞懂了整个的流程,以下做一些笔记和自己的简略概括总结
这里附上原文地址,再次感谢作者 一文读懂Android View事件分发机制
首先确定几个面试官可能会问到的问题

  1. 事件分发机制的分发过程
  2. onTouchEvent,onTouch方法的区别
  3. 如何去理解dispatchTouchEvent 和 onInterceptTouchEvent,onTouchEvent
  4. 如何使用requestDisallowInterceptTouchEvent方法,如何调用这个方法可以解决子view和父view之间的冲突
  5. 各个方法的返回值都代表了什么,怎么去理解

一 事件分发机制的分发过程

引用原文一张流程图,秒懂:
事件分发流程
简而言之,就是Activity的dispatchTouchEvent()–>Window–>DecorView(顶级的ViewGrop)的dispatchTouchEvent()点击事件传到了 ViewGrop里,之后再由viewgrop传入他自己的子view里

ViewGrop:可以有很多子view ,所以有dispatchTouchEvent, onInterceptTouchEvent,onTouchEvent(除了分发和触摸事件,还有拦截事件)
View:只有dispatchTouchEvent ,onTouchEvent(只有分发和触摸事件)

参照源码可以更加方便的便于理解:

我们模拟一个情景,来方便理解,引用https://blog.csdn.net/qq_30379689/article/details/53967177这篇文章中的情形

在这里插入图片描述

<?xml version="1.0" encoding="utf-8"?>                --------------------- 作者:Hensen_ 来源:CSDN 原文:https://blog.csdn.net/qq_30379689/article/details/53967177 版权声明:本文为博主原创文章,转载请附上博文链接!

文章中只是说出了假如我们重写各个方法会出现的情况,并没有说明具体流程,下面我来根据view的源码,分析一下自己的思考过程。
直接分析viewgrop的源码,我把他大致简化一下

 public boolean dispatchTouchEvent(MotionEvent ev) {        //省略        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 方法可以设置 FLAG_SPLIT_MOTION_EVENTS 这个标签,来控制是否拦截事件                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;            }            // 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 preorderedList = buildTouchDispatchChildList();                        final boolean customOrder = preorderedList == null                                && isChildrenDrawingOrderEnabled();                        final View[] children = mChildren;                        //这里是遍历所有的子view                        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,使得newTouchTarget 不为null                            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);                            //事件在这个dispatchTransformedTouchEvent方法里进行分发                            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();                                //给mFirstTouchTarget赋值                                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.            //如果mFirstTouchTarget 不为空,即 这个viewgrop有子view,            if (mFirstTouchTarget == null) {                // No touch targets so treat this as an ordinary view.                // 事件在这个dispatchTransformedTouchEvent方法里进行分发,表示事件还未消耗                handled = dispatchTransformedTouchEvent(ev, canceled, null,                        TouchTarget.ALL_POINTER_IDS);            } else {              //省略            }          //省略        return handled;    }
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;        }        //省略    }
 private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);        target.next = mFirstTouchTarget;        mFirstTouchTarget = target;        return target;    }

View 的dispatchTouchEvent方法,在ViewGrop类里的dispatchTransformedTouchEvent方法里进行调用,调用方式:super.dispatchTouchEvent(event);

  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)) {                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;    }

1、dispatchTouchEvent(分发事件)

如果在MyViewGroup01的dispatchTouchEvent方法中返回true,表示需要在MyViewGroup01消费了整个事件,即不会再分发,也不会再处理。dispatchTouchEvent方法中返回true的打印信息

//分发过程
MyViewGroup02 dispatchTouchEvent
MyViewGroup02 onInterceptTouchEvent
MyViewGroup01 dispatchTouchEvent

如果在MyViewGroup01的dispatchTouchEvent方法中返回false,表示在MyViewGroup01点击事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费。dispatchTouchEvent方法中返回false的打印信息

//分发过程
MyViewGroup02 dispatchTouchEvent
MyViewGroup02 onInterceptTouchEvent
MyViewGroup01 dispatchTouchEvent
//处理过程
MyViewGroup02 onTouchEvent

作者:Hensen_
来源:CSDN
原文:https://blog.csdn.net/qq_30379689/article/details/53967177
版权声明:本文为博主原创文章,转载请附上博文链接!

分析:当MyViewGroup01的dispatchTouchEvent为true的时候,MyViewGroup02的dispatchTouchEvent 和onInterceptTouchEvent按照顺序执行,MyViewGroup01 的dispatchTouchEvent也能执行,但是MyViewGroup01的 dispatchTouchEvent直接返回了true,所以在第一次调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)方法的时候(代码115行),就直接返回了true,所以接着又会执行newTouchTarget = addTouchTarget(child, idBitsToAssign)(代码132行)这个方法,这个方法则是给mFirstTouchTarget赋了值,所以当mFirstTouchTarget == null时候才会执行的handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)(代码158行)方法不会被执行,而最关键的view的onTouchEvent方法,就在chlid为null的时候会执行,所以当dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS)(代码161行)不执行的时候,就不会调用onTouchEvent方法,所以当MyViewGroup01的dispatchTouchEvent为true的时候,onTouchEvent不会执行,反之,则会执行

更多相关文章

  1. Android Studio3.0开发JNI流程------在Android原程序添加自己类
  2. 利用Android的Matrix类实现J2ME的drawRegion的镜像方法
  3. 调用Android自带日历功能(日历列表单、添加一个日历事件)
  4. Systrace 分析性能工具使用方法详解
  5. 简单的Android ROM制作方法:创建刷机包 备份ROM
  6. Android之A面试题④应用程序内部启动Activity过程(startActivity)
  7. Android 事件处理(―)(附源码)

随机推荐

  1. android google directions
  2. android使用shape设置下边框
  3. android 图片处理工具类
  4. Android中使用TabHost实现类似标签栏的效
  5. android表情Gson EditText TextView
  6. Android之查看手机实时电流、电压
  7. Andrpid Activity作Dialog使用
  8. android IntentService
  9. android字符串 优化(一)
  10. android http-post方法简单实现