编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 1.1App启动流程梳理
  • 1.2ActivityThread分析
  • 1.3Context设计思想
  • 2.1Activity基础介绍
  • 2.2Activity启动流程
  • 2.3Activity布局创建
  • 2.4Activity布局绘制
  • 2.5Service基础介绍
  • 2.6Service启动流程
  • 2.7Receiver广播基础
  • 2.8Receiver深入原理
  • 2.9ContentProvider分析
  • 2.10Fragment实践
  • 2.11Intent深入思考
  • 3.1Paint和Canvas
  • 3.2View的绘制基础
  • 3.3onMeasure流程设计
  • 3.4onLayout流程设计
  • 3.5onDraw流程设计
  • 3.6View工作原理
  • 3.7View刷新设计流程
  • 3.8自定义View控件
  • 3.9自定义ViewGroup控件
  • 4.1Handler基础使用
  • 4.2消息机制流程分析
  • 4.3Handler深度解析
  • 4.4Message深度理解
  • 4.5MessageQueue解析
  • 4.6Looper深度解析
  • 4.7理解Handler同步屏障
  • 4.8ThreadLocal分析
  • 4.9ThreadLocal场景
  • 5.1View事件设计思考
  • 5.2View滑动冲突处理
  • 5.3View事件源码分析
  • 5.4View事件总结案例
  • 6.1DecorView设计思想
  • 6.2视图的载体View
  • 6.3视图管理者Window
  • 6.4窗口管理服务WMS
  • 6.5布局解析者Inflater
  • 7.1AsyncTask深入介绍
  • 7.2HandlerThread设计
  • 7.3IntentService设计
  • 8.1IPC通信方式介绍
  • 8.2AIDL进程间通信
  • 8.7Binder通信机制设计
  • /zh/android/basic/9.1注解设计思想和原理.html
  • 9.2APT技术设计详解
  • 9.3APT多种案例实践

5.4View事件总结案例

目录介绍

  • 01.Android事件分发机制
  • 02.View和ViewGroup分发事件
  • 03.onTouch()、onTouchEvent()和onClick()
  • 04.事件的传递规则
  • 05.View处理事件的优先级
  • 06.点击事件传递过程
  • 07.事件传递规则要点

01.Android事件分发机制

  • 简述Android的事件分发机制?
    • 事件分发顺序:Activity->ViewGroup->View
    • 主要方法:dispatchTouchEvent-分发事件、onInterceptTouchEvent-当前View是否拦截该事件、onTouchEvent-处理事件
    • 1.父View调用dispatchTouchEvent开启事件分发。
    • 2.父View调用onInterceptTouchEvent判断是否拦截该事件,一旦拦截后该事件的后续事件(如DOWN之后的MOVE和UP)都直接拦截,不会再进行判断。
    • 3.如果父View进行拦截,父View调用onTouchEvent进行处理。
    • 4.如果父View不进行拦截,会调用子View的dispatchTouchEvent进行事件的层层分发。

02.View和ViewGroup分发事件

  • ViewGroup事件分发伪代码
    • image
      image
  • View事件分发伪代码
    • image
      image
  • View和ViewGroup在dispatchTouchEvent上的区别
    • ViewGroup在dispatchTouchEvent()中会进行事件的分发。
    • View在dispatchTouchEvent()中会对该事件进行处理。技术博客大总结

03.onTouch()、onTouchEvent()和onClick()

  • onTouch()、onTouchEvent()和onClick()关系是怎样的,哪一个先执行?
    • onTouch->onTouchEvent->onClick
      • 当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener的onTouch方法会被回调。
      • 这时事件如何处理还得看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被调用;如果返回true,那么onTouchEvent方法将不会被调用。由此可见,给View设置的onTouchListener,其优先级比onTouchEvent要高。
      • 如果当前方法中设置了onClickListener,那么它的onClick方法会被调用。可以看出,常用的OnClickListener,其优先级别最低。
  • 如果设置了onClickListener, 但是onClick()没有调用,可能产生的原因? 技术博客大总结
    • 父View拦截了事件,没有传递到当前View
    • View的Enabled = false(setEnabled(false)): view处于不可用状态,会直接返回。
    • View的Clickable = false(setClickable\setLongClickable(false)):view不可以点击,不会执行onClick
    • View设置了onTouchListener,且消耗了事件。会提前返回。
    • View设置了TouchDelegate,且消耗了事件。会提前返回。

04.事件的传递规则

  • 事件的传递规则是什么?
    • 点击事件产生后,会先传递给根ViewGroup,并调用dispatchTouchEvent
    • 之后会通过onInterceptTouchEvent判断是否拦截该事件,如果true,则表示拦截并交给该ViewGroup的onTouchEvent方法进行处理
    • 如果不拦截,则当前事件会传递给子元素,调用子元素的dispatchTouchEvent,如此反复直到事件被处理

05.View处理事件的优先级

  • View处理事件的优先级?
    • 在View需要处理事件时,会先调用OnTouchListener的onTouch方法,并判断onTouch的返回值
    • 返回true,表示处理完成,不会调用onTouchEvent方法
    • 返回false,表示未完成,调用onTouchEvent方法进行处理
    • 可见,onTouchEvent的优先级没有OnTouchListener高
    • onTouchEvent没有消耗的话就会交给TouchDelegate的onTouchEvent去处理。
    • 如果最后事件都没有消耗,会在onTouchEvent中执行performClick()方法,内部会执行OnClickListener的onClick方法,优先级最低,属于事件传递尾端

06.点击事件传递过程

  • 点击事件传递过程遵循如下顺序?技术博客大总结
    • Activity->Window->View->分发
    • 如果View的onTouchEvent返回false,则父容器的onTouchEvent会被调用,最终可以传递到Activity的onTouchEvent

07.事件传递规则要点

  • 事件传递规则要点?
    • View一旦拦截事件,则整个事件序列都由它处理(ACTION_DOWN\UP等),onInterceptTouchEvent不会再调用(因为默认都拦截了)
    • 但是一个事件序列也可以通过特殊方法交给其他View处理(onTouchEvent)
    • 如果View开始处理事件(已经拦截),如果不消耗ACTIO_DOWN事件(onTouchEvent返回false),则同一事件序列的剩余内容都直接交给父onTouchEvent处理
    • View消耗了ACTION_DOWN,但不处理其他的事件,整个事件序列会消失(父onTouchEvent)不会调用。这些消失的点击事件最终会传给Activity处理。
    • ViewGroup默认不拦截任何事件(onInterceptTouchEvent默认返回false)
    • View没有onInterceptTouchEvent方法,一旦有事件传递给View,onTouchEvent就会被调用
    • View的onTouchEvent默认都会消耗事件return true, 除非该View不可点击(clickable和longClickable同时为false)
    • View的enable属性不影响onTouchEvent的默认返回值。即使是disable状态。
    • onClick的发生前提是当前View可点击,并且收到了down和up事件
    • 事件传递过程是由父到子,层层分发,可以通过requestDisallowInterceptTouchEvent让子元素干预父元素的事件分发(ACTION_DOWN除外)

01.View滑动有哪些方法

  • View滑动有哪些方法?
    • layout:对View进行重新布局定位。在onTouchEvent()方法中获得控件滑动前后的偏移。然后通过layout方法重新设置。
    • offsetLeftAndRight和offsetTopAndBottom:系统提供上下/左右同时偏移的API。onTouchEvent()中调用
    • LayoutParams: 更改自身布局参数
    • scrollTo/scrollBy: 本质是移动View的内容,需要通过父容器的该方法来滑动当前View
    • Scroller: 平滑滑动,通过重载computeScroll(),使用scrollTo/scrollBy完成滑动效果。
    • 属性动画: 动画对View进行滑动技术博客大总结
    • ViewDragHelper: 谷歌提供的辅助类,用于完成各种拖拽效果。
  • Layout实现滑动
    • image
      image
  • offsetLeftAndRight和offsetTopAndBottom实现滑动
    • image
      image
  • LayoutParams实现滑动
    • 通过父控件设置View在父控件的位置,但需要指定父布局的类型,不好
    • 用ViewGroup的MariginLayoutParams的方法去设置margin
      //方法一:通过布局设置在父控件的位置。但是必须要有父控件, 而且要指定父布局的类型,不好的方法。 
      RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); 
      layoutParams.leftMargin = getLeft() + offsetX; 
      layoutParams.topMargin = getTop() + offsetY; 
      setLayoutParams(layoutParams);
      /**===============================================
       * 方法二:用ViewGroup的MarginLayoutParams的方法去设置marign
       * 优点:相比于上面方法, 就不需要知道父布局的类型。
       * 缺点:滑动到右侧控件会缩小
       *===============================================*/ 
      ViewGroup.MarginLayoutParams mlayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
      mlayoutParams.leftMargin = getLeft() + offsetX; 
      mlayoutParams.topMargin = getTop() + offsetY; 
      setLayoutParams(mlayoutParams);
  • scrollTo\scrollBy实现滑动
    • 都是View提供的方法。
    • scrollTo-直接到新的x,y坐标处。
    • scrollBy-基于当前位置的相对滑动。
    • scrollBy-内部是调用scrollTo.
    • scrollTo\scrollBy, 效果是移动View的内容,因此需要在View的父控件中调用。
    • scrollTo/By内部的mScrollX和mScrollY的意义
      • mScrollX的值,相当于手机屏幕相对于View左边缘向右移动的距离,手机屏幕向右移动时,mScrollX的值为正;手机屏幕向左移动(等价于View向右移动),mScrollX的值为负。
      • mScrollY和X的情况相似,手机屏幕向下移动,mScrollY为+正值;手机屏幕向上移动,mScrollY为-负值。
      • mScrollX/Y是根据第一次滑动前的位置来获得的,例如:第一次向左滑动200(等于手机屏幕向右滑动200),mScrollX = 200;第二次向右滑动50, mScrollX = 200 + (-50)= 150,而不是(-50)。
  • 动画实现滑动的方法
    • 可以通过传统动画或者属性动画的方式实现
    • 传统动画需要通过设置fillAfter为true来保留动画后的状态(但是无法在动画后的位置进行点击操作,这方面还是属性动画好)
    • 属性动画会保留动画后的状态,能够点击。博客
  • ViewDragHelper
    • 通过ViewDragHelper去自定义ViewGroup让其子View具有滑动效果。不过用的很少

02.Activity事件分发过程

  • Activity事件分发的过程?
    • 事件分发过程:Activity->Window->Decor View(当前界面的底层容器,setContentView的View的父容器)->ViewGroup->View
    • Activity的dispatchTouchEvent,会交给Window处理(getWindow().superDispatchTouchEvent()),
    • 返回true:事件全部结束
    • 返回false:所有View都没有处理(onTouchEvent返回false),则调用Activity的onTouchEvent

03.Window事件分发过程

  • Window事件分发?
    • Window和superDispatchTouchEvent分别是抽象类和抽象方法
    • Window的实现类是PhoneWindow
    • PhoneWindow的superDispatchTouchEvent()直接调用mDecor.superDispatchTouchEvent(),也就是直接传给了DecorView

04.DecorView的事件分发

  • DecorView的事件分发?
    • DecorView继承自FrameLayout
    • DecorView的superDispatchTouchEvent()会调用super.dispatchTouchEvent()——也就是ViewGroup的dispatchTouchEvent方法,之后就会层层分发下去。

05.根View的事件分发

  • 根View的事件分发?技术博客大总结
    • 顶层View调用dispatchTouchEvent
    • 调用onInterceptTouchEvent方法
    • 返回true,事件由当前View处理。如果有onTouchiListener,会执行onTouch,并且屏蔽掉onTouchEvent。没有则执行onTouchEvent。如果设置了onClickListener,会在onTouchEvent后执行onClickListener
    • 返回false,不拦截,交给子View重复如上步骤。

其他介绍

01.关于博客汇总链接

  • 1.技术博客汇总
  • 2.开源项目汇总
  • 3.生活博客汇总
  • 4.喜马拉雅音频汇总
  • 5.其他汇总

02.关于我的博客

  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yczbj/activities
  • 简书:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
  • 开源中国:https://my.oschina.net/zbj1618/blog
  • 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 邮箱:yangchong211@163.com
  • segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
贡献者: yangchong211
上一篇
5.3View事件源码分析
下一篇
6.1DecorView设计思想