安卓事件分发是个老生常谈的话题, 在ScrollView高度测量原理介绍Android View测量原理, 现在再用个实例讲解事件分发原理。

本文要搞懂2个问题。

1、 事件是怎么产生的?

2、事件是怎么传递的?

手指在屏幕上一滑, framework层的ViewRootImpl中广播监听WindowInputEventRecevier最先收到事件, 将native层传来的物理坐标转换成MotionEvent后继续传递。

事件传递过程可总结为 native层--->ViewRootImpl--->DecorView--->Activity--->PhoneWindow--->DecorView--->ViewGroup(可以理解为contentView)。 PS:记住这个顺序,很重要~~~。

dispatchTouchEvent:2604, ViewGroup (android.view)
superDispatchTouchEvent:549, DecorView (com.android.internal.policy)
superDispatchTouchEvent:1953, PhoneWindow (com.android.internal.policy)
dispatchTouchEvent:3548, Activity (android.app)
dispatchTouchEvent:21, EventActivity (com.byrcegao.myscrollviewtest.event)
dispatchTouchEvent:502, DecorView (com.android.internal.policy)
dispatchPointerEvent:12027, View (android.view)
processPointerEvent:5278, ViewRootImpl$ViewPostImeInputStage (android.view)
......
onInputEvent:7325, ViewRootImpl$WindowInputEventReceiver (android.view)
dispatchInputEvent:192, InputEventReceiver (android.view)
nativePollOnce:-1, MessageQueue (android.os)
next:379, MessageQueue (android.os)
loop:144, Looper (android.os)
main:7425, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:245, Zygote$MethodAndArgsCaller (com.android.internal.os)
main:921, ZygoteInit (com.android.internal.os)

小结:1、在Framework层的ViewRootImpl.java里产生了MotionEvent;  2、事件是从根节点传递到叶子节点(一般安卓视图是个多叉树结构)

上面是framework层的事件传递顺序,那么我们实际开发的View视图是如何传递的呢? 看个例子:  红色FrameLayout是祖父容器、绿色FrameLayout是父容器、蓝色TextView是子View(多叉树的叶子节点)。

假如手指在蓝色区域点击一下, 事件是如何传递的???  做个比喻, 事件传递就像西游记里猪八戒问大师兄你吃不吃, 大师兄不吃则被八戒吃掉了。

View包括dispatchTouchEvent(分发)和onTouchEvent(处理)函数, ViewGroup继承于View并新增了onInteruptTouchEvent(拦截器)函数。

时序 GrandView(ViewGroup派生类)

FatherView(ViewGroup派生类)

SonView(View派生类)
1 dispatchTouchEvent    
2 onInteruptTouchEvent    
3   dispatchTouchEvent  
4   onInterptTouchEvent  
5     dispatchTouchEvent
6     onTouchEvent
7   onTouchEvent  
8 onTouchEvent    
       

上面是事件传递的最简单模型, 画成表格后形状就像大于号。 从日志可以看到GrandView、FatherView和SonView只收到了ACTION_DOWN, 都没收到ACTION_UP事件。

PS:DOWN是事件的起点, 如果你不消费(消费就是返回true)DOWN事件则父容器不会再传递后续的MOVE/UP/CANCEL事件给自己。   原理可以查看ViewGroup的dispatchTouchEvent函数。

下面修改一下代码, FatherView拦截Down事件(在onInteruptTouchEvent返回true), SonView还能收到事件吗?  答案是不能。

public class FatherView extends FrameLayout {...@Override public boolean onInteruptTouchEvent(MotionEvent ev) {Log.d("brycegao", "FatherView dispatchTouchEvent:" + ev.toString());if (ev.getAction() == MotionEvent.ACTION_DOWN) {return true;  //子View不会收到任何事件}return onInteruptTouchEvent(ev);}...
}

在SonView的父容器FatherView里拦截Down事件后, SonView不会收到任何事件。

删除FatherView的拦截down逻辑, 在FatherView的onTouchEvent函数里消费down事件。 又会怎么样呢?

public class FatherView extends FrameLayout {... @Override public boolean dispatchTouchEvent(MotionEvent ev) {Log.d("brycegao", "FatherView dispatchTouchEvent:" + ev.toString());return super.dispatchTouchEvent(ev);}@Override public boolean onInterceptTouchEvent(MotionEvent ev) {Log.d("brycegao", "FatherView onInterceptTouchEvent:" + ev.toString());return super.onInterceptTouchEvent(ev);}@Override public boolean onTouchEvent(MotionEvent ev) {Log.d("brycegao", "FatherView onTouchEvent:" + ev.toString());if (ev.getAction() == MotionEvent.ACTION_DOWN) {return true;}return super.onTouchEvent(ev);}}

消费down事件后当前view可以收到后续的move、up事件, 且消费事件后就不需要再继续传递了(GrandView的onTouchEvent没收到down,因为被FatherView消费了)。从日志看到SonView仍然无法收到move、up事件。

应用层的事件传递逻辑,都在ViewGroup的dispatchTouchEvent函数里。

总结:

1、View的事件传递是自根节点向叶子节点询问, 如果被某个节点消费了(onTouchEvent或onTouch回调返回true),就不再执行父容器的onTouchEvent函数。 (从根节点DecorView到叶子节点的某个View消费了事件)

2、如果拦截了Down事件(onInteruptTouchEvent函数返回true), 则子节点不会收到任何事件;

3、如果当前View消费了Down事件(onTouchEvent函数或onTouch回调返回true), 则当前View才能收到后续的Move、Up和Cancel事件;

坑: onTouch和onTouchEvent的关系???

在View.java的dispatchTouchEvent函数里处理了onTouch回调, 执行时序dispatchTouchEvent---onTouch回调---onTouchEvent, 即onTouch回调在前,onTouchEvent在后。

如果onTouch返回true,则不会执行当前视图和父容器的onTouchEvent函数。

 /*** Interface definition for a callback to be invoked when a touch event is* dispatched to this view. The callback will be invoked before the touch* event is given to the view.*/public interface OnTouchListener {/*** Called when a touch event is dispatched to a view. This allows listeners to* get a chance to respond before the target view.** @param v The view the touch event has been dispatched to.* @param event The MotionEvent object containing full information about*        the event.* @return True if the listener has consumed the event, false otherwise.*/boolean onTouch(View v, MotionEvent event);}

一个小Demo带你理解安卓事件分发相关推荐

  1. java线程间通信:一个小Demo完全搞懂

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...

  2. springboot mybatis easyui 整合的一个小demo

    springboot mybatis easyui 整合的一个小demo 这是最终完成界面 话不多说 开整! 这是项目结构 数据库 表结构和数据库 (有点乱 之前本来是个正经图书表的 = =.) /* ...

  3. cuda编程与gpu并行计算(三):一个小demo了解cuda基本语法

    gpu程序的一般步骤 CPU分配空间给GPU(cudaMalloc) CPU复制数据给GPU(cudaMemcpy) CPU加载kernels给GPU做计算(Kernel核: 可以理解为C/C++中的 ...

  4. java线程间通信_java线程间通信:一个小Demo完全搞懂

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...

  5. Jmeter Web 性能测试入门 (四):一个小实例带你学会 Jmeter 脚本编写

    测试场景: 模拟并发100个user,在TesterHome 站内搜索VV00CC 添加线程组 添加HTTP信息头管理器 添加HTTP Sampler 填写HTTP Sampler中的信息 添加监听器 ...

  6. windows环境下使用clion引入eigen库并实现一个小demo

    本文仅作个人记录用,之前未接触过C++,也没有使用过clion和cmake,不喜勿喷. 首先下载clion,mingw并配置相关环境,安装cmake,这部分比较简单,没有遇到什么问题. 接下来新建一个 ...

  7. Android系统分析之带着问题看事件分发机制

    Android 触摸事件分发机制? Android系统分析之带着问题看事件分发机制 一 事件分发机制 1 什么是事件分发机制? 1.1 什么是事件? 答:当用户触摸屏幕时,每一次的点击,按下,移动,抬 ...

  8. mess-cli的一个小DEMO,微服务脚手架的实践!

    介绍 mess-cli是一个基于single-spa进行封装了的微服务脚手架,它为你提供了react.vue项目的single-spa模板 不了解mess-cli的,可以参考mess-cli官网,或者 ...

  9. cocos 时间函数需要什么引用_Cocos实战篇[3.4]——仿COC的一个小Demo总结

    [唠叨] 今天结束了本学期任务最为艰巨的项目实训课程,由于项目组里其他成员基本都已经找到实习了,然后他们都去实习了.只留下我和一个小伙伴在一起搞项目实训的小游戏.经过一个月与小伙伴的配合开发,做了一个 ...

最新文章

  1. Runtime 系列 3-- 给 category 添加属性
  2. PyCharm修改镜像源无用?
  3. 2021年李永乐6套卷一道无穷小定义的题目
  4. 图文详解 Kubernetes,刺激…
  5. 【ES6(2015)】Symbol
  6. Windows 10 再爆 Bug;罗永浩怼苹果失去灵魂;马化腾回应系 PS | CSDN 极客头条
  7. exchange 2003 event id 1221
  8. java线程main异常,‘java.lang.NoSuchMethodError:main的原因’线程中的异常“main”’...
  9. 菜鸟上路-Web开发模式
  10. linux每日命令(28):chgrp命令
  11. tabbar角标 小程序_关于小程序tabbar不支持传参的处理办法
  12. FRM 5.1 现代投资组合理论
  13. 华为手机日历倒计时_华为手机日历日程提醒不响怎么解决?
  14. C#-WinForm登录窗体实现记住密码的功能(仿QQ实现)
  15. 分享一个做AR很好用的软件
  16. 推荐系统实践(项亮)读书笔记
  17. 数分可视化笔记整理4 - 2017年内地电影和票房数据情况(条形图)
  18. Javaweb之核心技术(绘话技术)
  19. HEOI2013 ALO
  20. android7.1自带壁纸,RK3399 Android7.1 修改壁纸

热门文章

  1. pcs增加mysql资源_PCS --- 常用命令及配置
  2. 分布式限流的解决方案
  3. (转)七个受用一生的心理寓言
  4. Word中更新图表所有的域
  5. Android 自定义viewpage + videoview 实现竖屏视频播放效果
  6. C语言_公倍数、最小公倍数_调用函数求最大公因数最小公倍数
  7. 如何在Visual Studio中自动格式化代码?
  8. 分布式基础——GFS(Google file system)
  9. J2ME 粒子系统实现(转)
  10. *ptr++、*++ptr、++*ptr和(*ptr)++