作者:网易·周龙

最近刚看完android-Ultra-Pull-To-Refresh下拉刷新的源码,发现在写自定义控件时,对于View的事件的传递总是搞不太清楚,而View事件的分发机制,又是解决可能出现的滑动冲突的基础,因此专门学习了一下,结合一个例子来好好分析分析。

先上Demo,很简单,就是一个自定义ViewGroup和一个自定义View。View分发机制中,有三个很重要的方法是:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent,它们分别对应着事件的分发、拦截和消耗,因此我在自定义View的这三个方法里加上Log,根据打印的日志来分析下具体的流程。

public class CustomViewGroup extends LinearLayout {    private static final String TAG = "Mylog";    public CustomViewGroup(Context context) {        super(context);}    public CustomViewGroup(Context context, AttributeSet attrs) {        super(context, attrs);}    @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.d(TAG, "customViewGroup.dispathcTouchEvent "+ev.toString());        return super.dispatchTouchEvent(ev);}    @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.d(TAG, "customViewGroup.onInterceptTouchEvent "+ev.toString());        return super.onInterceptTouchEvent(ev);//        return  true;}    @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "customViewGroup.onTouchEvent "+event.toString());        return super.onTouchEvent(event);//        return  true;}    @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);}
}public class CustomView extends View {    private static final String TAG = "Mylog";    public CustomView(Context context) {        super(context);}    public CustomView(Context context, AttributeSet attrs) {        super(context, attrs);}    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);}    @Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.d(TAG, "customview.dispatchtouchevent "+event.toString());        return super.dispatchTouchEvent(event);}    @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "customview.onTouchEvent "+event.toString());//        return  true;return super.onTouchEvent(event);}
}customView.setOnTouchListener(new View.OnTouchListener() {            @Overridepublic boolean onTouch(View v, MotionEvent event) {Log.d(TAG, "customView.onTouch "+event.toString());                return false;}});customViewGroup.setOnTouchListener(new View.OnTouchListener() {            @Overridepublic boolean onTouch(View v, MotionEvent event) {Log.d(TAG, "customViewGroup.onTouch "+event.toString());                return false;}});}    @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.d(TAG, "Activity.dispatchTouchEvent "+ev.toString());        return super.dispatchTouchEvent(ev);}    @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "Activity.onTouchEvent "+event.toString());        return super.onTouchEvent(event);}

在Activity界面,我对CustomView和CustomViewGroup设定了onTouch监听。

下图是界面的一个示意图,Activity的界面包含了一个ViewGroup,而ViewGroup里包含了一个View,最简单的线性布局。


事件传递的基本原则:对于一个根ViewGroup来说,点击事件产生后,首先会传递给自身,并调用其dispatchTouchEvent方法,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着调用onTouchEvent来处理这个事件;如果这个ViewGroup不拦截,则将事件传递给子元素,子元素调用其onInterceptTouchEvent方法,如此反复直到最终被处理。

同一事件序列:指手指接触屏幕起,到手指离开屏幕的那一刻,在这个过程中产生的一系列动作。一般以down事件开始,一系列move事件,并以一个up事件结束。

可以表示为:[down, move,move,move......up]

在上述的基本原则下,我们来根据例子分析下View的事件分发过程。

  • 在默认情况下,点击区域3 View,Activity消耗事件

打印出来的Log如上,事件ACTION_DOWN先从Activity的dispatchTouchEvent开始传递,向下分发,由于Activity和ViewGroup默认都是不拦截事件的,因此不会拦截,一直传递到最内侧的View,注意到View的onTouch方法比其onTouchEvent方法先调用,这表示onTouch具有更高的优先级。由于我默认设置view也不处理事件,因此事件最终回到了Activity的onTouchEvent中,并接受了ACTION_UP事件。这表明一旦一个View接受了ACTION_DOWN事件,那么接下来的同一序列的事件都将交给它处理。结合下图可以更好的来理解。

  • 内部View消耗ACTION_DOWN事件

将代码中,CustomView的onTouchEvent返回值为true,则表示可以对接受到的事件进行消耗。我们点击区域3,并滑动一段距离,此时打印log如下:

可以看到,当View的onTouchEvent返回true时,当ACTION_DOWN事件分发到View后,就不在会向父层转发,表示此View将会对事件经行处理,之后的ACTION_MOVE等同一序列的事件,都将沿着之前的路径转发给此View。

  • 外部ViewGroup消耗ACTION_DOWN事件

将CustomView的onTouchEvent返回改成super.onTouchEvent,并将CustomViewGroup中的onTouchEvent和onInterceptTouchEvent返回均设为true,表示将由ViewGroup来拦截事件,并进行处理。我们点击内部的区域3 View:得到的打印logo如下:

从log中,我们可以发现,一旦ViewGroup准备拦截事件,则事件将不再被转发给View,因此View的dispatchTouchEvent等函数均不会被触发,另外,在之后的同一序列事件中,ViewGroup的onInterceptTouchEvent函数也不会被触发,这表示:一旦某个ViewGroup决定拦截事件,则系统不再会询问接下来的事件ViewGroup是否要拦截。

  • 点击区域2 ViewGroup

这种情况比较简单,此时因为点击的区域中,不包含其他View,因此ViewGroup将不会把事件转发给ViewGroup中的其他子View。而在Activity和ViewGroup中正常的进行事件分发,这里给出打印的logo,流程图示意省略。

  • 事件经过View后,在由ViewGroup处理

这种情况相对少见一点。我们需要将ViewGroup的onInterceptTouchEvent设为super,onTouchEvent设为true,View的onTouchEvent设为super. 这保证了事件将会被传递给子View,但子View不会去消耗这个事件。同样点击区域3 View后,logo如下:

和预期的一致,事件正常的分发到了View当中,但是子View并没有处理事件,而是交回到ViewGroup来处理。值得注意的是,之后同一序列的其他事件,都不在会分发给View,而是直接交给ViewGroup的onTouch方法去处理。这意味着一个View一旦接收了事件的转发而不处理,那么系统将不会把同一序列的其他事件转发给它。

大概情况就这么几种,常见的一般是直接由ViewGroup来消耗或者View消耗事件。

这里主要还是从应用层表面来分析了一些View事件的分发过程,如果想深入了解各个函数的实现,可以去阅读一下View的源码,从而更好的证实上述的结果。另外,注意到,View(不是ViewGroup)是不包含onInterceptTouchEvent方法的,这表示只要有事件传递给View,其onTouchEvent方法就会被触发,而ViewGroup是默认不拦截任何事件的,其onInterceptTouchEvent方法默认返回false。

在了解了View的事件分发机制后,就可以对一些出现滑动冲突的情况进行自己的处理,之后的文章中会继续写写学习心得。

· END ·

网易云信∣真正稳定的IM云服务

ID:neteaseim  长按关注,精彩不断

Android View的事件分发机制解析相关推荐

  1. Android View的事件分发机制和滑动冲突解决方案

    这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...

  2. 安卓自定义View进阶-事件分发机制原理【转自 app架构师 微信公众号】

    注意:本文中所有源码分析部分均基于 API23(Android 6.0) 版本,由于安卓系统源码改变很多,可能与之前版本有所不同,但基本流程都是一致的. 为什么要有事件分发机制? 安卓上面的View是 ...

  3. View体系与自定义View(三)—— View的事件分发机制

    1. 分析Activity的构成 一个Activity包含一个Window对象,这个对象是由PhoneWindow来实现的.PhoneWindow将DecorView作为整个应用窗口的根View. 而 ...

  4. 安卓自定义View进阶-事件分发机制原理

    之前讲解了很多与View绘图相关的知识,你可以在 安卓自定义View教程目录 中查看到这些文章,如果你理解了这些文章,那么至少2D绘图部分不是难题了,大部分的需求都能满足,但是关于View还有很多知识 ...

  5. 自定义View(二)--表层浅析View的事件分发机制和滑动冲突

    转载请注明出处:From李诗雨:http://blog.csdn.net/cjm2484836553/article/details/54387722 不诗意的女程序猿不是好厨师~ 这篇文章来得有些曲 ...

  6. Android View体系(五)从源码解析View的事件分发机制

    Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Sc ...

  7. 安卓自定义View进阶-事件分发机制详解

    原文地址:http://www.gcssloop.com/customview/dispatch-touchevent-source Android 事件分发机制详解,在上一篇文章 事件分发机制原理  ...

  8. Android事件分发机制解析

    博客地址juexingzhe欢迎关注 今天结合流程图和代码来对Android事件分发机制做一个总结,我自己起一个叫法就是"3个3". 跟事件分发相关的主要有三个节点方法: 1.di ...

  9. 对Android view/viewgroup事件分发的理解

    首先看看讲事件分发的博客: http://blog.csdn.net/xiaanming/article/details/21696315 和 http://www.csdn123.com/html/ ...

最新文章

  1. java sql sum函数的使用方法_SQL常用汇总函数用法说明
  2. 你一定要了解的NB-IoT !
  3. zip版mysql5.6_mysql 5.6 压缩包版安装方法
  4. 培训课程第三期签到和意见发表
  5. HTML颜色代码表/颜色名(网摘)
  6. 我对C++的一些疑问
  7. SQL2008数据类型
  8. [转载] python下载安装教程
  9. 【转载】Android S5PV210 fimc驱动分析 - fimc_regs.c
  10. c语言 对排两个答案文件
  11. Matplotlib笔记(莫烦Python)
  12. 【手把手教安装】VUE安装教程!!!
  13. Github渗透测试工具库
  14. RAC修改IP(public/virtual/scan)
  15. SubmitButton
  16. 华为认证hcip怎么找工作?考取华为认证hcip证书可以做什么?
  17. bzoj1050 [HAOI2006]旅行comf(并查集)
  18. typora生成目录
  19. 预测移动用户人口属性的Kaggle竞赛作品解析
  20. 嵩天老师《Python语言程序设计》第6周测试题笔记

热门文章

  1. 各浏览器抗uaf机制
  2. [Leetcode][JAVA] Reorder List
  3. 如何将数据导入到 SQL Server Compact Edition 数据库中(四)
  4. zabbix frontends php,zabbix 3.0.3 安装
  5. R语言教程:生存分析
  6. 目标检测--边界框(bounding box)解析
  7. Protues 8.6 详细安装步骤
  8. java同步锁实例_Java lock同步锁使用实例解析
  9. 编程之美系列之一——阶乘的运算
  10. linux tar压缩包目录,如何在Linux上使用tar命令解压和压缩文件