事件分发

  • 当点击事件发生时,事件最先传递给Activity,Activity会首先将事件将被所属的Window进行处理,即调用 superDispatchTouchEvent() 方法。通过观察superDispatchTouchEvent()方法的调用链,我们可以发现事件的传递顺序:
  1. PhoneWinodw.superDispatchTouchEvent()
  2. DecorView.dispatchTouchEvent(event)
  3. ViewGroup.dispatchTouchEvent(event)
  • 事件一层层传递到了ViewGroup里。
  • 每到一个子view,看他的onInterceptTouchEvent 方法是否拦截,ontouch是否消费方法,如果没有继续向下dispatchTouchEvent分发事件,都不处理向上传,当回到顶级,若顶层(activity)也不对此事件进行处理,此事件相当于消失了(无效果)。
  • View没有onInterceptTouchEvent()方法,有dispatchTouchEvent,一但有点击事件传递给它,它的ouTouchEvent()方法就会被调用。
  • ouTouchEvent是否消费事件取决于 ACTION_DOWN 事件 或 POINT_DOWN 事件是否返回 为true

递归

ViewGroup(View).dispatchTouchEvent()

  • ViewGroup.onInterceptTouchEvent()
  • child.dispatchTouchEvent()
  • super.dispatchTouchEvent()
    • View.onTouchEvent()

一个场景

有一个 ViewGroup, 然后手指头接触 Button ,手指头滑开了,滑开又松手的过程,整个事件发生了什么?经历了什么?

一开始ViewGroup 会接受到整个事件序列的第一个事件: ACTION_DOWN,ViewGroup#dispatchTouchEvent 收到ACTION_DOWN 后,

  开始询问 ViewGroup#onInterceptTouchEvent 是否需要拦截,

  默认情况下 ViewGroup#onInterceptTouchEvent 返回false 不拦截,开始向下传递ACTION_DOWN 事件,

  Buttton#dispatchTouchEvent 收到ACTION_DOWN 询问onTouchEvent 是否处理,

  Button 默认处理,此后的所有事件序列都直接跨过 ViewGroup#onInterceptTouchEvent 的判断直接传递给Button,

  但 ViewGroup#dispatchTouchEvent 会收到所有事件。随着手指的滑动Button 的坐标发生了改变,当手指抬起时触发 Button#onClick 事件。(移动出自己的范围,就消失了)

事件冲突

不同向嵌套

  1. 外部处理,重写父view的onInterceptTouchEvent ,MotionEvent的事件全部返回false,不拦截;
  2. 内部处理。重写子view的dispatchTouchEvent,通过requestDisallowInterceptTouchEvent方法(这个方法可以在子元素中干预父元素的事件分发过程),请求父控件不拦截自己的事件,true是不拦截,false是拦截。

同向嵌套

父 View 会彻底卡住子 View

原因:抢夺条件一致,但 父 View 的 onInterceptTouchEvent() 早于子View 的 dispatchTouchEvent()

本质上是策略问题:嵌套状态下用户手指滑动,他是想滑谁?

解决⽅方案:

实现策略—父 View、子 View 谁来消费事件可以实时协商

  1. 换成 NestedScrollView:可以滑动
  2. 实现 NestedScrollingChild3 接口来实现自定义的嵌套滑动逻辑

自定义单 View 的触摸反馈

View.onTouchEvent()

  • 当用户按下(ACTION_DOWN):

    • 如果不在滑动控件中,切换至按下状态,并注册长按计时器
    • 如果在滑动控件中,切换至预按下状态,并注册按下计时器
  • 当进入按下状态并移动(ACTION_MOVE):
    • 重绘 Ripple Effect
    • 如果移动出自己的范围,自我标记本次事件失效,忽略后续事件
  • 当用户抬起(ACTION_UP):
    • 如果是按下状态并且未触发长按,切换至抬起状态并触发点击事件,并清除⼀切状态
    • 如果已经触发长按,切换至抬起状态并清除一切状态
  • 当事件意外结束(ACTION_CANCEL):
    • 切换至抬起状态,并清除一切状态

View.dispatchTouchEvent()

View中 setOnTouchListener的onTouch,onTouchEvent,onClick的执行顺序

View的dispatchTouchEvent源码

public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
} if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
}

当以下三个条件任意一个不成立时

  1. mOnTouchListener不为null
  2. view是enable的状态
  3. mOnTouchListener.onTouch(this, event)返回true

    函数会执行到onTouchEvent。在这里我们可以看到,首先执行的是mOnTouchListener.onTouch的方法,然后是onTouchEvent方法继续追溯源码,到onTouchEvent()观察,发现在处理ACTION_UP事件里有这么一段代码
if (!post(mPerformClick)) {        performClick();    }

此时可知,onClick方法也在最后得到了执行所以三者的顺序是:

  1. setOnTouchListener() 的onTouch
  2. onTouchEvent()
  3. onClick()

view的事件分发:View为啥会有dispatchTouchEvent方法?

View可以注册很多事件监听器,事件的调度顺序是onTouchListener> onTouchEvent>onLongClickListener> onClickListener

自定义 ViewGroup 的触摸反馈

  • 除了重写 onTouchEvent() ,还需要重写 onInterceptTouchEvent()
  • onInterceptTouchEvent() 不用在第一时间返回 true,而是在任意事件,需要拦截的时候返回 true 就行
    //伪代码
view.dispatchTouchEvent(); public boolean dispatchTouchEvent(MotionEvent event) {
return ontouchEvent();
} ViewGroup.dispatchTouchEvent(); public boolean dispatchTouchEvent(MotionEvent event) {
boolean result;
if (interceptTouchEvent()) {
result = ontouchEvent();
} else {
result = 子 view的 dispatchTouchEvent ();
}
return result;
}

ViewGroup.dispatchTouchEvent()

  • 如果是用户初次按下(ACTION_DOWN),清空 TouchTargets 和DISALLOW_INTERCEPT 标记
  • 拦截处理
  • 如果不拦截并且不是 CANCEL 事件,并且是 DOWN 或者 POINTER_DOWN,尝试把 pointer(手指)通过 TouchTarget 分配给子 View;并且如果分配给了新的子 View,调用 child.dispatchTouchEvent() 把事件传给子 View
  • 看有没有 TouchTarget
    • 如果没有,调用⾃己的 super.dispatchTouchEvent()
    • 如果有,调用 child.dispatchTouchEvent() 把事件传给对应的子 View(如果有的话)
  • 如果是 POINTER_UP,从 TouchTargets 中清除 POINTER 信息;如果是 UP 或CANCEL,重置状态

TouchTarget

作用:记录每个子 View 是被哪些 pointer(手指)按下的

结构:单向链表

一般自定义onTouchEvent方法流程

  1. 在down的时候去记录坐标点

    getX/getY获取相对于当前View左上角的坐标,getRawX/getRawY获取相对于屏幕左上角的坐标。比如接触到按钮时,x,y是相对于该按钮左上点的相对位置。而rawx,rawy始终是相对于屏幕的位置。
  2. move的时候计算偏移量,并用scrollTo()或scrollBy()方法移动view。这俩个方法都是快速滑动,是瞬间移动的。注意:滚动的并不是viewgroup内容本身,而是它的矩形边框。
  3. 在up的时候,判断应显示的页面位置,并计算距离、滑动页面。

Android触摸反馈的更多相关文章

  1. Android 触摸事件处理机制

    Android 触摸事件的处理主要涉及到几个方法:onInterceptTouchEvent(), dipatchTouchEvent(), onTouchEvent(), onTouch(). on ...

  2. Android 触摸手势基础 官方文档概览

    Android 触摸手势基础 官方文档概览 触摸手势检测基础 手势检测一般包含两个阶段: 1.获取touch事件数据 2.解析这些数据,看它们是否满足你的应用所支持的某种手势. 相关API: Moti ...

  3. Android 触摸手势基础 官方文档概览2

    Android 触摸手势基础 官方文档概览 触摸手势检测基础 手势检测一般包含两个阶段: 1.获取touch事件数据 2.解析这些数据,看它们是否满足你的应用所支持的某种手势. 相关API: Moti ...

  4. iOS 和 Android 触摸事件传递

    先看文章,写得很好 ios 触摸事件传递 http://www.cnblogs.com/Quains/p/3369132.html 另外一篇 http://blog.csdn.net/yongyinm ...

  5. 对于android触摸事件模型的一些理解

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  6. 初识Android触摸事件传递机制

    前言 今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套Li ...

  7. Android触摸事件的应用

    前言 上一篇讲了Android触摸事件的传递机制,具体可以看这里 初识Android触摸事件传递机制.既然知道Android中触摸事件的传递分发,那么它能解决什么样的问题,在我们实际开发中如何应用,这 ...

  8. Android触摸事件传递机制

    简单梳理一下Android触摸事件传递机制的知识点. 一.View与ViewGroup的关系 View和ViewGroup二者的继承关系如下图所示: View是Android中最基本的一种UI组件,它 ...

  9. Android 触摸提示音【转】

    本文转载自:http://blog.csdn.net/Jin_HeZai/article/details/46791567 近期任务,涉及Android触摸提示音. 首先,定位源码目标.很显然的,在原 ...

随机推荐

  1. 根据多个成对的cron表达式生成的时间段,合并

    场景:数据库一张表,有个startcron 和endcron 两个字段,根据表达式计算今天的所有时间段. 例:startcron :0 30 20 ? * * endcron :0 30 21 ? * ...

  2. Spring Boot实战之定制type Formatters

    本文首发于个人网站:Spring Boot实战之定制type Formatters 前面我们有篇文章介绍了PropertyEditors,是用来将文本类型转换成指定的Java类型,不过,考虑到Prop ...

  3. [考试反思]1026csp-s模拟测试89:不公

    稍垃圾.因为T1没A. 赶巧前一段时间学了杜教筛,结果因为教练放错题. 然后考场上疯狂yy,最后水到了一个AC. 其实的确挺不公平的,不少人也没学呢. 如果只算T1和T3的分数的话,那70分就是个垃圾 ...

  4. 如何在 Spring/Spring Boot 中做参数校验?你需要了解的都在这里!

    本文为作者原创,如需转载请在文首著名地址,公众号转载请申请开白. springboot-guide : 适合新手入门以及有经验的开发人员查阅的 Spring Boot 教程(业余时间维护中,欢迎一起维 ...

  5. 奇怪的道路——状压DP

    题目描述 小宇从历史书上了解到一个古老的文明.这个文明在各个方面高度发达,交通方面也不例外. 考古学家已经知道,这个文明在全盛时期有n座城市,编号为1..n.m条道路连接在这些城市之间,每条道路将两个 ...

  6. CSS3解决字母不换行的方法

    CSS3解决字母不换行的方法 <pre>word-wrap: break-word;</pre>

  7. ARP通信

    ARP:地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议 简单介绍ARP通信过程: 1.发送端在与接收端进行数据通信转发时的过程: 发送端与接收端进行数据通信之前,需要先知道对端的MAC ...

  8. Jsp自学2

    Jsp简单来说就是java代码与Html代码的组合,类,方法,属性跟网页展示夹杂在一起.Jsp就是Servlet,但比Servle简单,不需要配置web.xml(当然也可以配置).Jsp由模板数据与元 ...

  9. [LC]26题 Remove Duplicates from Sorted Array (删除排序数组中的重复项)(双指针法)(原地实现)

    ①中文题目 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成 ...

  10. Django 基本使用

    Django 基本使用 Django 安装 pip install django Django 创建项目 django-admin startproject 项目名称 Django 创建应用 pyth ...