首先Android的事件分发是基于责任链设计模式的 如果不理解责任链设计 可以参考:
https://blog.csdn.net/u011109881/article/details/59631314
提示:本文结合自己的代码实践更有助于理解

一 几个典型案例

public class MyView extends View {private static final String TAG = "MyView";public MyView(Context context) {this(context, null);}public MyView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.d(TAG, "onClick: ");}});this.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {Log.d(TAG, "onTouch: " + event.getAction());return false;}});}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "onTouchEvent: " + event.getAction());return super.onTouchEvent(event);}
}

此时打印log如下

12-19 15:09:35.292  8259  8259 D MyView  : onTouch: 0
12-19 15:09:35.292  8259  8259 D MyView  : onTouchEvent: 0
12-19 15:09:35.341  8259  8259 D MyView  : onTouch: 2
12-19 15:09:35.341  8259  8259 D MyView  : onTouchEvent: 2
12-19 15:09:35.359  8259  8259 D MyView  : onTouch: 2
12-19 15:09:35.359  8259  8259 D MyView  : onTouchEvent: 2
12-19 15:09:35.374  8259  8259 D MyView  : onTouch: 2
12-19 15:09:35.374  8259  8259 D MyView  : onTouchEvent: 2
12-19 15:09:35.391  8259  8259 D MyView  : onTouch: 2
12-19 15:09:35.391  8259  8259 D MyView  : onTouchEvent: 2
12-19 15:09:35.413  8259  8259 D MyView  : onTouch: 2
12-19 15:09:35.413  8259  8259 D MyView  : onTouchEvent: 2
12-19 15:09:35.459  8259  8259 D MyView  : onTouch: 2
12-19 15:09:35.459  8259  8259 D MyView  : onTouchEvent: 2
12-19 15:09:35.459  8259  8259 D MyView  : onTouch: 1
12-19 15:09:35.459  8259  8259 D MyView  : onTouchEvent: 1
12-19 15:09:35.459  8259  8259 D MyView  : onClick:

按照责任链的思路 似乎onTouch的出在责任链发起端 onTouchEvent接着 最后是 onClick事件

二 分析原因 View的事件分发源码分析

二.一 OnTouchListener中的onTouch返回值可以决定onTouchEvent是否可以走到

    //事件分发的起点public boolean dispatchTouchEvent(MotionEvent event) {...//是否消费 false意味着不消费 事件继续向责任链后端传递// true意味着消费事件(吃掉事件) 事件无法向责任链后端传递boolean result = false;...if (actionMasked == MotionEvent.ACTION_DOWN) {//手势为下落的时候 停掉嵌套view的滚动// Defensive cleanup for new gesturestopNestedScroll();}if (onFilterTouchEventForSecurity(event)) {if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {result = true;}//noinspection SimplifiableIfStatementListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {//要执行onTouch 需要满足条件//1 li != null 2 li.mOnTouchListener != null 3 (mViewFlags & ENABLED_MASK) == ENABLEDresult = 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;}

根据我们之前的分析 来到责任链的最前端 line22的onTouch事件
1 li != null
2 li.mOnTouchListener != null
3 (mViewFlags & ENABLED_MASK) == ENABLED
要执行这个代码 需要三个条件
为此我们要先看看li的类型ListenerInfo究竟是个啥

    static class ListenerInfo {/*** Listener used to dispatch focus change events.* This field should be made private, so it is hidden from the SDK.* {@hide}*/@UnsupportedAppUsageprotected OnFocusChangeListener mOnFocusChangeListener;/*** Listeners for layout change events.*/private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;protected OnScrollChangeListener mOnScrollChangeListener;/*** Listeners for attach events.*/private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;/*** Listener used to dispatch click events.* This field should be made private, so it is hidden from the SDK.* {@hide}*/@UnsupportedAppUsagepublic OnClickListener mOnClickListener;/*** Listener used to dispatch long click events.* This field should be made private, so it is hidden from the SDK.* {@hide}*/@UnsupportedAppUsageprotected OnLongClickListener mOnLongClickListener;/*** Listener used to dispatch context click events. This field should be made private, so it* is hidden from the SDK.* {@hide}*/protected OnContextClickListener mOnContextClickListener;/*** Listener used to build the context menu.* This field should be made private, so it is hidden from the SDK.* {@hide}*/@UnsupportedAppUsageprotected OnCreateContextMenuListener mOnCreateContextMenuListener;@UnsupportedAppUsageprivate OnKeyListener mOnKeyListener;@UnsupportedAppUsageprivate OnTouchListener mOnTouchListener;@UnsupportedAppUsageprivate OnHoverListener mOnHoverListener;@UnsupportedAppUsageprivate OnGenericMotionListener mOnGenericMotionListener;@UnsupportedAppUsageprivate OnDragListener mOnDragListener;private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;OnCapturedPointerListener mOnCapturedPointerListener;private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;private WindowInsetsAnimationListener mWindowInsetsAnimationListener;/*** This lives here since it's only valid for interactive views.*/private List<Rect> mSystemGestureExclusionRects;/*** Used to track {@link #mSystemGestureExclusionRects}*/public RenderNode.PositionUpdateListener mPositionUpdateListener;}

我们看到ListenerInfo里面有我们熟悉的OnClickListener以及mOnTouchListener 那么我们在代码里调用setOnClickListener setOnTouchListener源码做了什么呢

    public void setOnClickListener(@Nullable OnClickListener l) {if (!isClickable()) {setClickable(true);}getListenerInfo().mOnClickListener = l;}public void setOnTouchListener(OnTouchListener l) {getListenerInfo().mOnTouchListener = l;}@UnsupportedAppUsageListenerInfo getListenerInfo() {if (mListenerInfo != null) {return mListenerInfo;}mListenerInfo = new ListenerInfo();return mListenerInfo;}

哦吼,发现了ListenerInfo是一系列Listener的集合,在我们调用setOnTouchListener的时候有两个条件已经满足
1 li != null 2 li.mOnTouchListener != null 那么剩下的条件3 (mViewFlags & ENABLED_MASK) == ENABLED如何判断呢
(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,默认都是enable的(这点我是听视频里面说的 有个实验可以验证这一点 就是在代码里面调用View的setEnabled(false)方法 发现即使注册了OnTouchListener 里面的onTouch也不会执行了
说到这里前面三个条件都成立了 那么li.mOnTouchListener.onTouch(this, event)开始执行,这就调用到我们代码里面的onTouch代码块了,而他的返回值决定了result(是否消费/吃掉事件),也就是事件是否继续执行,如果我们在onTouch返回了false(不吃掉事件)result维持默认值false,那么事件继续传递 line28 if (!result && onTouchEvent(event))得以继续执行,否则result=true onTouch无法继续执行(可以实验在OnTouchListener 里面的onTouch返回true看看

二.二onTouchEvent中的返回值可以决定onClick是否可以走到

同样是基于之前责任链设计思路 onClick必定在onTouchEvent之后调用,那么是在onTouchEvent内部呢还是在onTouchEvent调用之后呢?我们查看onTouchEvent的内部代码可以发现答案是前者

    public boolean onTouchEvent(MotionEvent event) {...if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {switch (action) {case MotionEvent.ACTION_UP:...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();}}}...break;case MotionEvent.ACTION_DOWN:...break;case MotionEvent.ACTION_CANCEL:...break;case MotionEvent.ACTION_MOVE:...break;}return true;}return false;}private final class PerformClick implements Runnable {@Overridepublic void run() {recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP);performClickInternal();}}private boolean performClickInternal() {// Must notify autofill manager before performing the click actions to avoid scenarios where// the app has a click listener that changes the state of views the autofill service might// be interested on.notifyAutofillManagerOnClick();return performClick();}public boolean performClick() {// We still need to call this method to handle the cases where performClick() was called// externally, instead of through performClickInternal()notifyAutofillManagerOnClick();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;}

可以看到在MotionEvent.ACTION_UP会调用PerformClick方法 其中会performClickInternal,最后调用的performClick中会调用到onClick事件
因此总结下来 View的事件分发的顺序是这样的
dispatchTouchEvent->OnTouchListener onTouch DOWN事件->onTouchEvent DOWN事件
->OnTouchListener onTouchMOVE事件->onTouchEvent MOVE事件
->OnTouchListener onTouchUP事件->onTouchEventUP事件
->OnClickListener onClick
其中任意一个环节 事件被吃掉(返回true)则后续的链子都断掉(收不到后续事件),不过这仅限于后面的层级,(不是直接把链条中间断开,后面的事件都接收不到的意思)
换一种说法,我们可以可以将OnTouchListener onTouchEvent OnClickListener 想象成3层不透光的纸,上面有个开关,return false可以打开开关 让光投到下一层,在上面的一系列事件中,比如我们 OnTouchListener onTouch MOVE的时候关闭了开关,那么后面两层的事件都无法接受到后续事件,但是注意,OnTouchListener onTouchUP事件还是可以接收到的,开关只影响后面的层级。说到这个开关 其实只有OnTouchListener onTouchEvent 有而已,活用这个开关,可以让我们在事件中只接受特定的事件。
https://blog.csdn.net/fengluoye2012/article/details/83782042
这里有个参考链接 里面的图片很适合理解事件分发

红橙Darren视频笔记 View事件分发源码分析 基于API29相关推荐

  1. 红橙Darren视频笔记 ViewGroup事件分发分析 基于API27

    本节目标,通过案例,先看程序运行结果,然后跟踪源码,理解为什么会有这样的输出,继而理解view group的分发机制,感觉和证明题很像呢. 考虑以下程序的运行结果: case1: public cla ...

  2. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 四 | View 事件传递机制 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  3. 【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) 文章目录 ...

  4. 【Android 事件分发】ItemTouchHelper 事件分发源码分析 ( 绑定 RecyclerView )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  5. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  6. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  7. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  8. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 三 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  9. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

最新文章

  1. jsp ajax动态添加数据,jquery Ajax实现Select动态添加数据
  2. 电子科技大学2019年计算机复试分数线,2019考研电子科技大学复试分数线已公布...
  3. [转]使用xcode4 workspace 多个project协同工作
  4. c语言中队列的作用,循环队列的实现(C语言)
  5. SQL SERver2005中row_number() 的用法
  6. Ping 命令完全讲解
  7. 51单片机数值存储c语言教程,单片机c语言教程:C51变量
  8. VC 2008下安装与配置OpenCV2.1
  9. 小程序微信支付errcode:40163问题
  10. CSS固定定位(position: fixed;/background-attachment: fixed;)
  11. [腾讯社区开放平台]介绍开放授权协议-OAuth
  12. PCL教程-PCLVisualizer可视化类的使用
  13. shiny改写服务器文件,Shiny生产环境部署与共享
  14. CocosCreator开场CG动画制作
  15. 哈工大CSAPP大作业 2022
  16. 计算机内存和u盘存储原理,解剖U盘存储结构原理
  17. Node.js阶段学习(一)
  18. mysql-删除语句汇总
  19. 会计未来十年发展趋势_CFO眼中未来十年财务变化【值得收藏】
  20. ubuntu16.04纯净系统配置安装集锦

热门文章

  1. 随便选一张扑克牌_教大家怎么快速记忆扑克牌?一学就会
  2. mysql数据库连接锁住_锁mysql方法
  3. lol云顶之奕助手_关于云顶之奕的感想
  4. Day7:html和css
  5. Bootstrap 警告框(Alert)插件
  6. D. The Door Problem 带权并查集
  7. PYTHON 自动化学习之路
  8. Unity脚本各种[XXX]的用法
  9. 【AC自动机】HDU 2222 Keywords Search 裸题
  10. Unity3d Shader开发(三)Pass(Fog )