Kotlin 学习笔记(二)—— 基础语法


Kotlin学习笔记系列教程

Kotlin 学习笔记(一)—— 概述、学习曲线、开发工具、参考资料


1. 基础语法

定义包

包的声明应该处于源文件顶部:

1
2
3
4
package hard.uistudy.dai.uifinaltest.main.view.fragment
import android.os.Bundle
import android.support.v4.app.Fragment

定义函数

带有两个Int 参数、返回Int 的函数:

1
2
3
fun addNumber(a : Int, b: Int) : Int {
return a + b
}

将表达式作为函数体、返回值类型自动推断的函数:

1
fun addNumber(a: Int, b: Int) = a + b

函数返回无意义的值:

1
2
3
fun printSum(a: Int, b: Int): Unit {
println("sum of $a and $b is ${a + b}")
}

Unit 返回类型可以省略:

1
2
3
fun printSum(a: Int, b: Int) {
println("sum of $a and $b is ${a + b}")
}

定义变量

一次赋值(只读不可写)的局部变量,使用val修饰:

1
2
val a : Int =5 //立即赋值
val text = "aaa" //自动推断类型

可变变量(可读可写),使用var修饰:

1
2
3
4
5
6
7
8
9
var b = 5 // 自动推断出 `Int` 类型
b = 6
/**
* 定义暂不赋值,lateinit不能用在可空的属性上和java的基本类型上,lateinit只能修饰var
*/
lateinit var open : String
...
open = "aaa"

顶层变量:

1
2
3
4
5
6
val PI = 3.14
var x = 0
fun incrementX() {
x += 1
}

注释

同Java一样,kotlin也支持行注释 及 块注释

1
2
3
4
// 这是一个行注释
/* 这是一个多行的
块注释。 */

使用字符串模板

使用$符号

1
2
3
4
5
6
7
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"

使用条件表达式

1
2
3
4
5
6
7
fun maxOf(a: Int, b: Int): Int {
if (a > b) {
return a
} else {
return b
}
}

使用 if 作为表达式:

1
fun maxOf(a: Int, b: Int) = if (a > b) a else b

使用可空值及 null 检测

当某个变量的值可能为null时,必须在声明处的类型后添加?来标识该引用可为空。
如果text的内容不是数字返回null:

1
2
3
fun parseInt(string: String) : Int?{
return if (string.toInt() != null) string.toInt() else null
}

使用返回可空值的函数:

1
2
3
4
5
6
7
8
9
10
11
fun getProduct(str1: String, str2: String){
val x = parseInt(str1)
val y = parseInt(str2)
if (x != null && y != null){
print(x + y)
} else{
println("either '$str1' or '$str2' is not a number")
}
}

或者 分别分析

1
2
3
4
5
6
7
8
9
10
11
12
// ……
if (x == null) {
println("Wrong number format in arg1: '$arg1'")
return
}
if (y == null) {
println("Wrong number format in arg2: '$arg2'")
return
}
// 在空检测后,x 和 y 会自动转换为非空值
println(x * y)

使用类型检测及自动类型转换

is 运算符检测一个表达式是否为某类型的一个实例。如果一个不可变的局部变量或者属性已经判断出为某类型,那么检测后的 分支直接当做该类型使用,无需显示转换:

1
2
3
4
5
6
7
8
fun getStrigLength(obj:Any): Int?{
if (obj is String){
// `obj` 在该条件分支内自动转换成 `String`
return obj.length
}
// 在离开类型检测分支后,`obj` 仍然是 `Any` 类型
return null
}

或者

1
2
3
4
5
6
fun getStringLength(obj: Any): Int? {
if (obj !is String) return null
// `obj` 在这一分支自动转换为 `String`
return obj.length
}

甚至

1
2
3
4
5
6
7
8
fun getStringLength(obj: Any): Int? {
// `obj` 在 `&&` 右边自动转换成 `String` 类型
if (obj is String && obj.length > 0) {
return obj.length
}
return null
}

使用 for 循环

1
2
3
4
5
6
7
val list = arrayListOf("1","2","3","4")
fun printList(){
for (item in list){
print(item)
}
}

或者

1
2
3
4
5
6
7
val list = arrayListOf("1","2","3","4")
fun printList(){
for (index in list.indices){
print("$list at $index value is ${list[index]} " )
}
}

使用While循环

1
2
3
4
5
6
7
8
9
val list = arrayListOf("1","2","3","4")
fun printList(){
var index = 0
while (index < list.size){
print("$index,${list[index]}")
index++
}
}

使用When循环

1
2
3
4
5
6
7
8
fun printList(index : Int) : String =
when (index ){
1 -> "$index value is 1"
2 -> "$index value is 2"
3 -> "$index value is 3"
4 -> "$index value is 4"
else -> "null"
}

使用区间(range)

使用 in 运算符来检测某个数字是否在指定区间内:

1
2
3
4
5
6
7
8
val startIndex = 10
val endIndex = 20
fun sortNum(){
if (startIndex in 1.. endIndex){
print("$startIndex is in range")
}
}

区间迭代:

1
2
3
4
5
fun sortNum(){
for (index in startIndex .. endIndex){
print(index)
}
}

数列迭代

1
2
3
4
5
6
7
8
9
//每次跳过3个元素
for (index in startIndex .. endIndex step 3){
print(index)
}
//降序遍历,每次跳过3个元素
for (index in endIndex downTo startIndex step 3){
}

检测某个数字是否在指定区间外:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range too")
}
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range too")
}

使用集合

对集合进行迭代:

1
2
3
4
5
6
7
val lists = listOf("a", "b", "c")
fun sortList(){
for (item in lists){
print(item)
}
}

使用in 运算符来判断集合内是否包含某实例:

1
2
3
4
5
6
7
8
val lists = listOf("a", "b", "c")
fun sortList(){
when{
"a" in lists -> print("a")
"b" in lists -> print("b")
}
}

使用lambda 表达式来过滤(filter)和映射(map)集合:

1
2
3
4
5
6
7
8
val lists = listOf("aa", "ab", "ac","1","2","3","4")
fun sortList(){
lists.filter { it.startsWith("a")} //过滤出 a 开头的元素
.sortedBy { it } //排序
.map { it.toUpperCase() } //将字符串转为大写
.forEach { print(it) } //遍历输出字符串
}

创建基本类及其实例

1
2
val rectangele = Rectangle() //不需要 new 关键字
val triangle = Triangle()

>以上就是第二篇的全部内容,先学习到这里,第三篇继续学习基础教程

个人博客地址:http://outofmemory.top/
CSDN地址:http://blog.csdn.net/dazhaoDai
GitHub地址:https://github.com/dazhaoDai


Kotlin 学习笔记(一)—— 概述、学习曲线、开发工具、参考资料


Kotlin学习笔记系列教程

Kotlin 学习笔记(一)—— 概述、学习曲线、开发工具、参考资料


1. Kotlin 概述

简介

Kotlin 是一门支持多范式、多平台的现代静态编程语言。Kotlin 支持面向对象、泛型与函数式等编程范式,它支持 JVM、Android、JavaScript 目标平台,而原生(Native)平台的 Kotlin 几天前也发布了 0.2 版本。而且 Kotlin 具有很多现代(也有称下一代的)静态语言特性:如类型推断、多范式支持、可空性表达、扩展函数、模式匹配等。因此上面描述毫不夸张,它是一门非常有潜力的新兴语言。

另外 100% 的 Java 互操作性,使 Kotlin 能够与既有工具/框架如 Dagger、Spring、Vert.x 等集成,也能让既有的基于 Java 的服务端与 Android 项目逐步迁移到 Kotlin。

设计目标

创建一种兼容Java的语言,让它比Java更安全,能够静态检测常见的陷阱。如:引用空指针
让它比Java更简洁,通过支持variable type inference,higher-order functions (closures),extension functions,mixins and first-class delegation等实现。
让它比最成熟的竞争对手Scala语言更加简单

总结:Kotlin就是一个基于JVM,可以兼容Java并且比Java更简洁,能够静态检测常见陷阱的的新的编程语言

2017年Google I/O正式将Kotlin列为官方开发语言

2. 学习曲线

和大多数编程语言一样,学习由浅及深
从 基础语法 -> 类与对象 -> 函数表达式 -> 类型检查 -> 核心库 -> Java操作

3. 开发工具

Intellij IDEA是由JetBrains开发,而且Kotlin就是JetBrains开发的语言,所以毫无疑问 Intellij IDEA
是最合适不过的开发工具。
Android StudioIntellij IDEA的插件实现的Android开发IDE,同理支持Kotlin开发,特别是AndroidStudio3.0更新后,Google官方默认支持Kotlin,无需插件。

4. 参考资料

Kotlin中文站
Kotlin知乎专栏
Kotlin 语言官方参考文档 中文版


第一篇文章简单介绍kotlin简介、开发工具、学习资料等基础,后面将继续学习Kotlin基础


个人博客地址:http://outofmemory.top/
CSDN地址:http://blog.csdn.net/dazhaoDai
GitHub地址:https://github.com/dazhaoDai


Android自定义View(八) -- 硬件加速

前面学习的内容:
Android自定义View(一) – 初识
Android自定义View(二) – Paint详解
Android自定义View(三) – drawText()
Android自定义View(四) – Canvas
Android自定义View(五) – 绘制顺序
Android自定义View(六) – 属性动画(上)
Android自定义View(七) – 属性动画(下)


本文计划根据HenCoder系列文章进行学习,所以代码风格及博文素材可能会摘自其中


硬件加速经常被提及,很多人感兴趣,这个词给人的概念大概有两种:快速、不稳定。

对很多人来说,硬件加速似乎是一个只可远观而不可亵玩的高科技:是,听说很牛逼,但是不敢乱用,甚至不知道什么时候使用

今天就试着把硬件加速的原理和应用,好好了解一下:

1.硬件加速的本质和原理;

2.硬件加速在Android中的应用;

3.硬件加速在Android正宗的限制。

概念

在正式开始之前需要说明一下,作为绘制部分的最后一期,本期内容只是为了内容的完整性做一个补充,因为之前好几期的内容里都有涉及硬件加速的技术点,而一些读者因为不了解硬件加速而产生了一些疑问。所以仅仅从难度上来讲,这期的内容并不难,并且本期的大部分内容你都可以从这两个页面中找到:

  1. Hardware Acceleration | Android Developers
  2. Google I/O 2011: Accelerated Android Rendering

下面进入正题。

所谓硬件加速,指的是把某些计算工作交给专门的硬件来做,而不是和普通的计算工作一样交给 CPU 来处理。这样不仅减轻了 CPU 的压力,而且由于有了「专人」的处理,这份计算工作的速度也被加快了。这就是「硬件加速」。

而对于 Android 来说,硬件加速有它专属的意思:在 Android 里,硬件加速专指把 View 中绘制的计算工作交给 GPU 来处理。进一步地再明确一下,这个「绘制的计算工作」指的就是把绘制方法中的那些 Canvas.drawXXX() 变成实际的像素这件事。

原理

在硬件加速关闭的时候,Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,然后在之后的渲染过程中,这个 Bitmap 的像素内容被直接用于渲染到屏幕。这种绘制方式的主要计算工作在于把绘制操作转换为像素的过程(例如由一句 Canvas.drawCircle() 来获得一个具体的圆的像素信息),这个过程的计算是由 CPU 来完成的。大致就像这样:

而开启硬件加速后,Canvas的工作方式改变了:它把绘制的内容转为GPU的操作保存下来,然后交给GPU来完成显示工作。大致过程:

如图,硬件加速开启时,CPU的工作是把绘制工作转换为GPU的操作,这个工作量相对来说还是非常小的。

怎么「加速」了

从上图可以看出,开启硬件加速后,绘制的计算工作有CPU交给GPU,不过这怎么就能起到加速作用,让绘制变快了呢?

硬件加速能够让绘制变快,主要有三个原因:

  1. 本来CPU的工作,分摊一部分给GPU,自然可以提高效率;
  2. 相对于CPU来说,GPU自身的设计本来就对于很多常见类型内容的计算(例如简单的圆形、方形)具有优势;
  3. 由于绘制流程的不同;硬件加速在界面内容发生重绘的时候绘制流程可以得到优化,避免一些重复操作,从而大幅提升绘制效率。

其中前两点可以总结为一句话:用了GPU,绘制就是快,原因很直观,不再多说。

关于第三点,它的原理大致说一下:

前面说到,关闭硬件加速时,绘制内容会被CPU转为实际的像素,然后直接渲染到屏幕,具体来说,这个[实际的像素],是由bitmap承载的,在界面的某个View由于内容发生改变而调用invalidat()方法时,如果没有开启硬件加速,为了正确计算bitmap的像素,这个View的父View、父View的父View乃至一直向上知道最顶级的View,以及所有和它相交的View,都需要被调用invalidate()来重绘,一个View的改变使得大半个界面甚至整个界面重绘一遍,这个工作量是非常大的。

而在开启硬件加速时,前面说过,绘制的内容会被转换成GPU的操作保存下来(承载的形式成为displaylist,对应的类也叫作DisplayList),再转交给GPU。由于所有绘制的内容都没有变成最终的像素,所以它们之间是相互独立的,那么在界面内容发生改变时,只需把发生了改变的View调用invalidate()方法以更新它所对应的GPU就好,至于它的父View和兄弟View,只需要保持原样,那么这个工作量就很小了。

正是由于上面的原因,硬件加速不仅是由于GPU的引入提高效率,而且因为绘制机制的改变,而极大的提高了界面内容改变时的刷新效率

所以把上面三条压缩总结一下,硬件加速更快的原因有两条:

  1. 用了GPU,绘制更快了
  2. 绘制机制的改变,导致界面内容改变时的刷新效率极大提高。

限制

如果仅仅是这样,硬件加速只有好处没有坏处,那大家不必要关心其他问题,直接使用就行了,那这篇文章也没有必要了,既然是好东西,关心那么多原理干嘛?

可事实就是,硬件加速不止有好处,也有限制:收到GPU绘制方式的限制,Canvas的有些方法在硬件加速开启时会失效或者无法正常工作,比如:开启硬件加速,clipPath()在 API 18及以上系统中才有效,具体的 API 限制和 API 版本的关系如下图:

所以,如果你对自定义控件有自定义绘制的内容,最好参照一下表格,确保你的绘制操作可以正确地在所有用户手机中正常显示,而不是只在你最新Android系统的 Nexus 或 Pixel 里测试一遍没问题就发布。那就小心被祭天了。

不过有一点可以放心的是,所有的原生自带控件,都没有用到 API 版本不兼容的绘制操作,可以放心使用。所以你只要检查你写的自定义绘制就好。

View Layer

在之前几期的内容里我提到过几次,如果你的绘制操作不支持硬件加速,你需要手动关闭硬件加速来绘制界面,关闭的方式是通过这行代码:

1
view.setLayerType(LAYER_TYPE_SOFTWARE, null);

很多人有过疑问:什么是layer type?如果这个方法是关闭硬件加速的开关,那么它的参数为什么不是一个LAYER_TYPE_SOFTWARE来关闭硬件加速以及一个LAYER_TYPE_HARDWARE来开启硬件加速,这两个参数,而是三个参数,第三个参数为LAYER_TYPE_NONE,难道还能既不用软件绘制又不用硬件绘制吗?

事实上,view.setLayerType(LAYER_TYPE_SOFTWARE, null)这个方法的作用并不是关闭硬件加速,只是当它的参数为LAYER_TYPE_SOFTWARE的时候,可以顺便把硬件加速关掉而已;并且除了这个方法外,Android并没有提供专门的View级别的硬件加速开关,所以它就顺便成了一个开关硬件加速的方法。

setLayerType() 这个方法,它的作用其实就是字面的意思:设置View Layer的类型。所谓ViewLayer,又称为离屏缓冲(off-screen Buffer),它的作用就是单独启用一块地方来绘制这个View,而不是使用绘制软件的Bitmap或者通过硬件加速的GPU,这块地方可能是一块单独的bitmap,也可能是一块OpenGL的纹理(texture,OpenGL的纹理可以简单理解为图像的意思),具体取决于硬件加速是否开启。采取什么来绘制View不是关键,关键在于当设置了View Layer的时候,它的绘制会被缓存下来,而且缓存的是最终的绘制结果,而不是像硬件加速那样只是把GPU的操作保存下来再交给GPU去计算。通过这样更进一步的缓存方式,View的重绘效率进一步提高了:只要绘制的内容没变,那么不论是CPU绘制还是GPU绘制,都不用重新计算,只要用之前缓存的结果就可以了。

多说一句,其实这个离屏缓冲(Off-screen Buffer),更准确的说应该叫做离屏缓存(Off-screen Cache)会更合适一点。原因在上面这一段里已经说过了,因为它其实是缓存而不是缓冲。(这段话仅代表个人意见)

基于这样的原理,在进行移动、旋转等(无需调用 invalidate())的属性动画的时候开启 Hardware Layer 将会极大地提升动画的效率,因为在动画过程中 View 本身并没有发生改变,只是它的位置或角度改变了,而这种改变是可以由 GPU 通过简单计算就完成的,并不需要重绘整个 View。所以在这种动画的过程中开启 Hardware Layer,可以让本来就依靠硬件加速而变流畅了的动画变得更加流畅。实现方式大概是这样:

1
2
3
4
5
6
7
8
9
10
11
view.setLayerType(LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setLayerType(LAYER_TYPE_NONE, null);
}
});
animator.start();

或者如果是使用 ViewPropertyAnimator,那么更简单:

1
2
3
view.animate()
.rotationY(90)
.withLayer(); // withLayer() 可以自动完成上面这段代码的复杂操作

不过一定要注意,只有你在对 translationX translationY rotation alpha 等无需调用 invalidate() 的属性做动画的时候,这种方法才适用,因为这种方法本身利用的就是当界面不发生时,缓存未更新所带来的时间的节省。所以简单地说——

这种方式不适用于基于自定义属性绘制的动画。一定记得这句话。

另外,除了用于关闭硬件加速和辅助属性动画这两项功能外,Layer 还可以用于给 View 增加一些绘制效果,例如设置一个 ColorMatrixColorFilter 来让 View 变成黑白的:

1
2
3
4
5
6
7
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
view.setLayerType(LAYER_TYPE_HARDWARE, paint);

另外,由于设置了ViewLayer后,View在初次绘制时以及每次invalidate()后重绘时,需要进行两次的绘制工作(一次绘制到Layer,一次从Layer绘制到显示屏),所以其实它的每次绘制的效率是被降低了的,所以一定要慎重使用View Layer,在需要用到它的时候再去使用。

总结

本期内容就是这些,就像文章开始说的,本期知识是作为一个完整的补充,并么有太多重要或者高难度的东西,我也没有准备视频或者太多的截图或者GIF 去说明,总结一下:

硬件加速指使用GPU来完成绘制的计算工作,代替CPU,它从工作分摊和绘制机制优化两个角度提升了绘制速度。

硬件加速可以使用setLayerType()来关闭硬件加速,但这个方法其实是用来设置View Layer的:

  1. 参数为 LAYER_TYPE_SOFTWARE 时,使用软件来绘制View Layer,绘制到一个Bitmap,并顺便关闭硬件加速;
  2. 参数为 LAYER_TYPE_HARDWARE 时,使用GPU来绘制View Layer,绘制到一个OpenGL texture(如果硬件加速关闭,那么行为和LAYER_TYPE_SOFTWARE一致);
  3. 参数为 LAYER_TYPE_NONE 时,关闭View Layer。

View Layer 可以加速无 invalidate() 时的刷新效率,但对于需要调用 invalidate() 的刷新无法加速。

View Layer 绘制所消耗的实际时间是比不使用 View Layer 时要高的,所以要慎重使用。

Git常用操作

前言:

Git作为日常开发的版本管理系统,功能强大,操作简单,简直不要太方便,但是,常用口令,却是我们必须要掌握的,比较AS安装Git插件,使用命令行才是最快捷方便的

正文:

开发中:常用操作就那么几个,那就一一罗列出来:

1.从远程服务器Clone分支

见我之前文章 Git从远端服务器Clone分支到本地

2.将本地分支推送到远端新分支

开发中遇到一种情况,本地分支Local1是从远端某分支Remote1 Clone下来的,但是开发之后,想将本地分支保存到远端的新分支Remote2上,而远端仓库是没有这个Remote2分支的,那么执行以下命令:git push origin <local_branch_name>:<remote_branch_name>
例如我当前操作:git push origin Local1:Remote2

3.删除本地仓库某个分支

想删除本地仓库某个分支,执行:git branch -D <local_branch_name>,例如:想删除本地仓库widget2分支,执行命令:git branch -D widget2

4.删除远端仓库某分支

删除远端仓库分支和删除本地类似:git push origin :{remote_branch_name},例如删除远端develop分支:git push origin :develop

Android6.0运行时权限,化繁为简

前言

权限申请效果

GIF.gif

GitHub地址PermissionUtils

Android8.0昨天已经发布,但是关于Android版本的最新统计,来看看图

image.png

很明显,6.0版本目前比重最高,从6.0开始的运行时权限,是每个Android开发者绕不过的问题

正文

虽然Google大佬允许我们将targetSdkVersion 设置为22及以下,但是向来紧跟Google大佬步伐的我们,怎么会用这种投机取消的方式呢。

关于Android权限,Google分为两类,一类为普通权限,默认在AndroidManifest中注册即可,主要为以下,这类权限默认不需要用户授权,比如震动、访问网络权限:

ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS

而另一类就是危险权限,涉及用户隐私或影响设备安全
根据表中可以看出,危险权限都是成组出现的,那么在授权时,如果申请一组中某个权限,权限申请弹窗会提示整组权限的说明,而且如果一组权限中的某个权限已经被授权,那么在申请同组其他权限时,系统立即授权,不需要通过用户允许。
注意: 运行时权限依然要在AndroidManifest中注册

Permission Group Permissions
CALENDAR READ_CALENDARWRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTSWRITE_CONTACTSGET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATIONACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATECALL_PHONEREAD_CALL_LOGWRITE_CALL_LOGADD_VOICEMAILUSE_SIPPROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMSRECEIVE_SMSREAD_SMSRECEIVE_WAP_PUSHRECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE

开始适配
这里举例适配Manifest.permission.ACCESS_FINE_LOCATION位置权限

  • 1、在AndroidManifest中注册权限
  • 2、检查权限

    1
    2
    3
    ContextCompat.checkSelfPermission(thisActivity,
    Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED
  • 3、申请权限

    1
    2
    3
    ActivityCompat.requestPermissions(thisActivity,
    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
    myRequestCode);
  • 4、申请回调的处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Override
    public void onRequestPermissionsResult(int requestCode,
    String permissions[], int[] grantResults) {
    switch (requestCode) {
    case myRequestCode: {
    if (grantResults.length > 0
    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    //申请通过
    } else {
    //申请失败
    //是否需要向用户解释申请的原因,在用户点击拒绝后,弹出dialog,给用户解释
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
    Manifest.permission.ACCESS_FINE_LOCATION))
    }
    }
    }
    }
    }

封装
运行时权限的流程很清晰,那么开发中每次写这么多固定代码,为何不进行封装一下呢?那就来分析申请的流程和特点,进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package com.ddz.lifestyle.utils;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.SparseArray;
import com.ddz.lifestyle.LifeStyle;
import com.ddz.lifestyle.http.GankApiStores;
import com.ddz.lifestyle.http.bean.BookBean;
import com.tencent.smtt.sdk.QbSdk;
import org.jetbrains.annotations.NotNull;
/**
* @Author: ddz
* Creation time: 17.8.11 17:11
* describe:{Android权限申请的封装,并在内部实现申请结果的处理}
*/
public class PermissionUtils {
private static PermissionUtils permission;
private String[] permissions;
private Activity mActivity;
private RequestPermissionListener PermissionListener;
public static int requestCode = 100; //requestCode传值为100
public static PermissionUtils getInstance() {
if (null == permission) {
synchronized (PermissionUtils.class) {
if (null == permission) {
permission = new PermissionUtils();
}
}
}
return permission;
}
/**
* 权限检查
*
* @param permission
* @return
*/
public boolean checkPermission(@NonNull String permission) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
} else {
return ContextCompat.checkSelfPermission(LifeStyle.getContext(), permission) == PackageManager.PERMISSION_GRANTED;
}
}
/**
* Activity 页面申请权限
*
* @param activity
* @param permissions
* @param requestCode
* @param requestPermissionListener
*/
public void requestPermissiion(final @NonNull Activity activity,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode, @NonNull RequestPermissionListener requestPermissionListener) {
this.mActivity = activity;
PermissionListener = requestPermissionListener;
this.permissions = permissions;
ActivityCompat.requestPermissions(activity, permissions, requestCode);
}
/**
* Fragment页面申请权限
*
* @param fragment
* @param permissions
* @param requestCode
* @param requestPermissionListener
*/
public void requestFragmentPermission(final @NonNull Fragment fragment,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode, @NonNull RequestPermissionListener requestPermissionListener) {
PermissionListener = requestPermissionListener;
this.permissions = permissions;
fragment.requestPermissions(permissions, requestCode);
}
/**
* 权限申请结果的回调
*
* @param activity
* @param requestCode
* @param permissions
* @param grantResults
*/
public void onRequestPermissionResult(final @NonNull Activity activity, int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
this.mActivity = activity;
if (null != PermissionListener) {
if (requestCode == PermissionUtils.requestCode && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
PermissionListener.requestConfirm();
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permissions[0])) {
PermissionListener.requestCancel();
} else {
PermissionListener.requestCancelAgain();
}
}
}
}
/**
* 用户点击拒绝,弹出申请权限的说明弹窗,也可以自定义实现
*
* @param context Context
* @param title 弹窗标题
* @param message 申请权限解释说明
* @param confirm 确认按钮的文字,默认OK
* @param cancel 取消按钮呢的文字,默认不显示取消按钮
*/
public void requestDialog(Context context, @NonNull String title, @NonNull String message, String confirm, String cancel) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle(title).setMessage(message);
builder.setPositiveButton(confirm == null ? "OK" : confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissiion(mActivity, permissions, requestCode, PermissionListener);
dialog.dismiss();
}
});
if (null != cancel) {
builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
builder.setCancelable(false);
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用户勾选不再显示并点击拒绝,弹出打开设置页面申请权限,也可以自定义实现
*
* @param context Context
* @param title 弹窗标题
* @param message 申请权限解释说明
* @param confirm 确认按钮的文字,默认OK
* @param cancel 取消按钮呢的文字,默认不显示取消按钮
*/
public void requestDialogAgain(Context context, @NonNull String title, @NonNull String message, String confirm, String cancel) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle(title).setMessage(message);
builder.setPositiveButton(confirm == null ? "OK" : confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startSettingActivity(mActivity);
dialog.dismiss();
}
});
if (null != cancel) {
builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
builder.setCancelable(false);
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打开设置页面打开权限
*
* @param context
*/
public void startSettingActivity(@NonNull Activity context) {
try {
Intent intent =
new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" +
context.getPackageName()));
intent.addCategory(Intent.CATEGORY_DEFAULT);
context.startActivityForResult(intent, 10); //这里的requestCode和onActivityResult中requestCode要一致
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打开设置页面的回调
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 10: //这里值是打开设置页面申请权限的RequestCode,默认为10
try {
if (null != PermissionListener) {
if (null != permissions && permissions.length > 0) {
for (String permission : permissions) {
if (checkPermission(permission)) {
PermissionListener.requestConfirm();
} else {
PermissionListener.requestFailed();
}
}
} else {
PermissionListener.requestFailed();
}
} else {
PermissionListener.requestFailed();
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
/**
* 权限申请回调
*/
public interface RequestPermissionListener {
void requestConfirm(); //申请成功
void requestCancel(); //拒绝
void requestCancelAgain(); //勾选不再提示并拒绝
void requestFailed(); //在设置页面申请权限失败
}
}

封装的工具类使用 (分为四步)

检查权限

1
PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)

申请权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申请成功
toast(null);
}
@Override
public void requestCancel() {
//用户拒绝,对用户解释申请理由
//如果想使用封装好的弹窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申请权限", "需要位置权限", null, null);
}
@Override
public void requestCancelAgain() {
//用户勾选不再提示并拒绝,申请打开应用设置页面申请权限,具体逻辑自己写
//使用默认封装好的提示,并打开设置页面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申请权限", "去设置页面打开位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申请失败
toast("对不起,没有权限,退出");
}
});

权限回调的处理

1
2
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);

打开设置页面申请权限的处理

1
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);

根据封装好的工具类,写个简单例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.ddz.lifestyle.view.activity;
import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.ddz.lifestyle.R;
import com.ddz.lifestyle.utils.PermissionUtils;
/**
* @Author: ddz
* Creation time: 17.8.11 16:07
* describe:()
*/
public class TestActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPremission();
}
private void initPremission() {
if (PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
toast(null);
} else {
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申请成功
toast(null);
}
@Override
public void requestCancel() {
//用户拒绝,对用户解释申请理由
//如果想使用封装好的弹窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申请权限", "需要位置权限", null, null);
}
@Override
public void requestCancelAgain() {
//用户勾选不再提示并拒绝,申请打开应用设置页面申请权限,具体逻辑自己写
//使用默认封装好的提示,并打开设置页面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申请权限", "去设置页面打开位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申请失败
toast("对不起,没有权限,退出");
}
});
}
}
//一定要对权限回调进行处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
//如果打开了设置页面申请权限,一定要对回调进行处理
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
private void toast(String message) {
Toast.makeText(TestActivity.this, message == null ? "权限申请成功" : message, Toast.LENGTH_SHORT).show();
}
}

结束

封装后的权限申请,结构更加清晰,根据回调结果的不同可以做出相应处理,也封装了权限申请的说明弹窗,如果没有特殊的设计要求,即可满足日常开发的权限申请工作。
GitHub地址:PermissionUtils

关于Android AIDL 编译报错问题

问题:

在做一个音乐项目中,使用到了AIDL跨进程通信组件,编写一个简单的AIDL时候,编译一直无法通过,报错内容

1
2
3
Error:Execution failed for task ':app:compileDebugAidl'.
java.lang.RuntimeException: com.android.ide.common.process.ProcessException: Error while executing process E:\SDK\build-tools\25.0.2\aidl.exe with arguments {-pE:\SDK\platforms\android-25\framework.aidl -oE:\Project\LifeStyle\app\build\generated\source\aidl\debug -IE:\Project\LifeStyle\app\src\main\aidl -IE:\Project\LifeStyle\app\src\debug\aidl -IC:\Users\yun.android\build-cache\bd2b26450777017e058ddff21ee3c3ce84e4a7bf\output\aidl -IC:\Users\yun.android\build-cache\2ea784cc81ce0926ea68faf50edaf3063b660d0e\output\aidl -IC:\Users\yun.android\build-cache\5b0533eca8fabee2573210d5a6db0ef9c0c55098\output\aidl -IC:\Users\yun.android\build-cache\4aff7d24ca88b5fbfe9cecf19662277ee6b67028\output\aidl -IC:\Users\yun.android\build-cache\b99b113aa0b77b7431d57c24286f1c8e8eab3ac8\output\aidl -IC:\Users\yun.android\build-cache\4529918ad206e483048c7e77ddeb67757631979d\output\aidl -IC:\Users\yun.android\build-cache\3044c80427502394b2a5a4f143d6cff3e658c021\output\aidl -IC:\Users\yun.android\build-cache\b5f3194e152b8cd07a05991ec812dffa1d8cb041\output\aidl -IC:\Users\yun.android\build-cache\38ea6e0a6326b10847dbffa39bb99db561e2d206\output\aidl -IC:\Users\yun.android\build-cache\8b120b0a8440e62fa16bb4d6105e23babda95bac\output\aidl -IC:\Users\yun.android\build-cache\f7ecc1e5d6327684b86b316a5b8d8656ab31a562\output\aidl -IC:\Users\yun.android\build-cache\555508f307eda5e323eab911e97c525ed5d5df39\output\aidl -IC:\Users\yun.android\build-cache\89763dd7cc6d960b99b1b369e1e4cacfb0a31de4\output\aidl -IC:\Users\yun.android\build-cache\7ade3d874bb02876c4dbec5f11f4f6e45fb740a9\output\aidl -IC:\Users\yun.android\build-cache\d146a875e535f7ef213ac298ff9c7562980017e2\output\aidl -IC:\Users\yun.android\build-cache\b7e59e7515f5465f0bdcddcb2373d90d2f734f9e\output\aidl -IC:\Users\yun.android\build-cache\7d9f182c861cca1c8561cf15680698fccb00cd6b\output\aidl -IC:\Users\yun.android\build-cache\700509b55aea82f4d612ac1262f62676e4b903a8\output\aidl -IC:\Users\yun.android\build-cache\abe96579d37055680246e88ec8e5202f311a7682\output\aidl -IC:\Users\yun.android\build-cache\6a4a8f6c11eb14ee244b1c31f215a78b80a8772f\output\aidl -IC:\Users\yun.android\build-cache\9acdeaf56617b78c0869aec47ac34c9f74876025\output\aidl -IC:\Users\yun.android\build-cache\7560a7d14537f4d91e192c3bf5482b4f43fa71b7\output\aidl -IC:\Users\yun.android\build-cache\f07b74a7bc4f977ae6e648a0b0c6273da95a15d3\output\aidl -IC:\Users\yun.android\build-cache\e916c7e89b0437bc49caaa56819cb6420b1df388\output\aidl -IC:\Users\yun.android\build-cache\98e02d16b6f5edc105354f1b8731dbb8996766a6\output\aidl -IC:\Users\yun.android\build-cache\0de94c2965cbd469e50e04f8b11f3f3a54fb105c\output\aidl -IC:\Users\yun.android\build-cache\e3570cee326ac234ab7a645a61cc67e5f4aaba25\output\aidl -IC:\Users\yun.android\build-cache\af33f12b5828ec7d29cdc70bd0113ea8de920816\output\aidl -IC:\Users\yun.android\build-cache\70727cf612c37ae60ba16fdf9dde9dd2be59839d\output\aidl -IC:\Users\yun.android\build-cache\a70928a5a1cbea562f1bd432d010447ec910b87c\output\aidl -IC:\Users\yun.android\build-cache\1e4a7240c43a860a0f8c255418d94983c100aacf\output\aidl -IC:\Users\yun.android\build-cache\17f28128f57674af28605d6308b0c053b944c5e8\output\aidl -IC:\Users\yun.android\build-cache\995b0dfa92490fcf0f8815d039702fbe5bce10a8\output\aidl -IC:\Users\yun.android\build-cache\15938a94ee6db7edc48dcf60da42083cf7a1bc6c\output\aidl -IC:\Users\yun.android\build-cache\29c97a53c8bc6293d81f08917bfd65d3f697d79e\output\aidl -IC:\Users\yun.android\build-cache\1005f94c0d904c86565f1d4db92c798c70837c1c\output\aidl -IC:\Users\yun.android\build-cache\bf586708fe17232a5869c4b61e020557d355d3d6\output\aidl -IC:\Users\yun.android\build-cache\9b42fe08f88e56de3c951e7c80c399b9f94078a1\output\aidl -IC:\Users\yun.android\build-cache\cd0824ff26bcc093fac07fa62e9800ef96d334aa\output\aidl -dC:\Users\yun\AppData\Local\Temp\aidl3827942310160614195.d E:\Project\LifeStyle\app\src\main\aidl\com\ddz\lifestyle\IPlayService.aidl}

绞尽脑汁,又是google又是度娘,都没有合适解决办法,最终发现有两个问题:

  • 实体类没有使用inoutinout 中任何一种方法修饰,导致编译失败

    后来为添加了in 修饰后,依然无法通过,最终发现问题所在

  • 在设置方法时候, 设置了同名不同参数方法,在AIDL中不可以这样命名,导致编译无法通过,懊恼不已,浪费大量时间

后记:

开发中一定要够自信,够清醒,不然真是浪费时间

Privacy Policy

隐私政策

本应用尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,本应用会按照本隐私权政策的规定使用和披露您的个人信息。但本应用将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,本应用不会将这些信息对外披露或向第三方提供。本应用会不时更新本隐私权政策。 您在同意本应用服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于本应用服务使用协议不可分割的一部分。

1. 适用范围

(a) 在您注册本应用帐号时,您根据本应用要求提供的个人注册信息;(b) 在您使用本应用网络服务,或访问本应用平台网页时,本应用自动接收并记录的您的浏览器和计算机上的信息,包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据;(c) 本应用通过合法途径从商业伙伴处取得的用户个人数据。您了解并同意,以下信息不适用本隐私权政策:(a) 您在使用本应用平台提供的搜索服务时输入的关键字信息;(b) 本应用收集到的您在本应用发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;(c) 违反法律规定或违反本应用规则行为及本应用已对您采取的措施。

2. 信息使用

(a)本应用不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和本应用(含本应用关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。(b) 本应用亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何本应用平台用户如从事上述活动,一经发现,本应用有权立即终止与该用户的服务协议。(c) 为服务用户的目的,本应用可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与本应用合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)。

3. 信息披露

在如下情况下,本应用将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:(a) 经您事先同意,向第三方披露;(b)为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;(c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;(d) 如您出现违反中国有关法律、法规或者本应用服务协议或相关规则的情况,需要向第三方披露;(e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;(f) 在本应用平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,本应用有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。(g) 其它本应用根据法律、法规或者网站政策认为合适的披露。

4. 信息存储和交换

本应用收集的有关您的信息和资料将保存在本应用及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本应用收集信息和资料所在地的境外并在境外被访问、存储和展示。

5. Cookie的使用

(a) 在您未拒绝接受cookies的情况下,本应用会在您的计算机上设定或取用cookies ,以便您能登录或使用依赖于cookies的本应用平台服务或功能。本应用使用cookies可为您提供更加周到的个性化服务,包括推广服务。(b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies,则您可能无法登录或使用依赖于cookies的本应用网络服务或功能。(c) 通过本应用所设cookies所取得的有关信息,将适用本政策。

6. 信息安全

(a) 本应用帐号均有安全保护功能,请妥善保管您的用户名及密码信息。本应用将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。(b) 在使用本应用网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对

7.本隐私政策的更改

(a)如果决定更改隐私政策,我们会在本政策中、本公司网站中以及我们认为适当的位置发布这些更改,以便您了解我们如何收集、使用您的个人信息,哪些人可以访问这些信息,以及在什么情况下我们会透露这些信息。(b)本公司保留随时修改本政策的权利,因此请经常查看。如对本政策作出重大更改,本公司会通过网站通知的形式告知。

方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是本应用用户名及密码发生泄露,请您立即联络本应用客服,以便本应用采取相应措施。

关于Android触摸事件机制

关于Android触摸事件机制

Android触摸事件机制,开发中都是老生常谈,但是惭愧的是,这么久开发,依然对Android触摸事件的具体流程,懵懵懂懂,趁着项目上线间隙,来重新研究一下。

准备

为了尽可能简单并清晰的展示Android触摸事件的 分发–拦截–消费过程,将根据Activity、ViewGroup以及View的特点,做了一点准备工作。

触摸事件:

  • actionDown
  • actionMove
  • actionUp

对于Android事件处理分为两类

对于Activity和View: 只有两种事件:

  • 分发: dispatchTouchEvent函数
  • 消费: onTouchEvent函数和OnTouchListener函数

对于ViewGroup: 全部三种事件:

  • 分发: dispatchTouchEvent函数
  • 拦截:onInterceptTouchEvent函数
  • 消费: onTouchEvent函数和OnTouchListener函数

所以,重写一个典型的ViewGroup:RelativeLayout和一个典型的View:TextView,针对他们的触摸事件进行改写,来得出触摸事件具体的流程。

TouchRelativeLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TouchRelativeLayout extends RelativeLayout {
public TouchRelativeLayout(Context context) {
super(context);
}
public TouchRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TouchRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtils.e("ViewGroup-------dispatchTouchEvent-------", ev.getAction() + "");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
LogUtils.e("ViewGroup-------onInterceptTouchEvent-------", ev.getAction() + "");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtils.e("ViewGroup-------onTouchEvent-------", event.getAction() + "");
return super.onTouchEvent(event);
}
}

TouchTextView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class TouchTextView extends TextView {
public TouchTextView(Context context) {
super(context);
}
public TouchTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtils.e("View---------dispatchTouchEvent-------", ev.getAction() + "");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtils.e("View---------onTouchEvent-------", event.getAction() + "");
return super.dispatchTouchEvent(event);
}
}

代码里不进行任何操作,只打印出当前控件的触摸事件。
继续新建一个Activity,为触摸事件提供入口:

Activity的布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<com.ddz.lifestyle.baseview.customview.TouchRelativeLayout
android:id="@+id/rl_touch"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="@android:color/holo_blue_light">
<com.ddz.lifestyle.baseview.customview.TouchTextView
android:id="@+id/tv_touch"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"
android:background="@color/recy_bg" />
</com.ddz.lifestyle.baseview.customview.TouchRelativeLayout>
</RelativeLayout>

忽略外层的RelativeLayout,实际就是一个Activity中,一个ViewGroup包裹一个View,Activity中不进行操作,只打印出触摸事件:

1
2
3
4
5
6
7
8
9
10
11
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LogUtils.e("activity-------dispatchTouchEvent-------", ev.getAction() + "");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
LogUtils.e("activity-------onTouchEvent-------", event.getAction() + "");
return super.onTouchEvent(event);
}

运行程序,打开要触摸的Activity,显示效果,蓝色区域为重写的TouchRelativeLayout,其中的灰色区域就是重写的TouchTextView

image

开始分析:

1、默认不改写任何事件

  • 1、触摸A区域(按下、抬起)
    1
    2
    3
    4
    08-10 14:58:08.131 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 14:58:08.132 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 14:58:08.697 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 14:58:08.697 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

根据日志分析:记录下了Activity的分发dispatchTouchEvent和onTouchEvent事件,0为按下动作,1为抬起动作。

得出结论:默认Activity将触摸事件分发下去,并且没有子View消费情况下, 自己消费;

  • 2、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    08-10 15:11:43.128 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 15:11:43.129 11780-11780/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 15:11:43.129 11780-11780/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 15:11:43.129 11780-11780/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 15:11:43.129 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 15:11:43.519 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:11:43.519 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:11:43.536 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:11:43.536 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:11:43.553 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:11:43.553 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:11:43.569 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:11:43.570 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:11:43.875 11780-11780/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 15:11:43.875 11780-11780/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:

1、按下后,Activity将事件分发给ViewGroup,ViewGroup返回默认super.dispatchTouchEvent(ev)将事件分发下去并传递给ViewGroup的onInterceptTouchEvent函数;

2、onInterceptTouchEvent返回默认的super.onInterceptTouchEvent(ev),事件传递给ViewGroup的onTouchEvent方法

3、onTouchEvent依然返回默认的super.onTouchEvent(event),不进行消费,将事件返回给上一层的Activity,Activity调用onTouchEvent,处理了按下事件。

4、手指移动,Activity的dispatchTouchEvent方法将事件分发。因为按下事件中,已经得知Activity中的ViewGroup不对触摸事件进行任何操作,并将事件返回给Activity,所以聪明的Activity这时候将分发后的事件自己消费了

5、Activity的onTouchEvent对移动事件自行处理

6、手指抬起:同样,Activity的dispatchTouchEvent方法将事件分发,自己的onTouchEvent进行消费

  • 3、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    08-10 15:50:20.933 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 15:50:20.933 20620-20620/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 15:50:20.934 20620-20620/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 15:50:20.934 20620-20620/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 15:50:20.934 20620-20620/com.ddz.lifestyle E/View---------onTouchEvent-------: 0
    08-10 15:50:20.943 20620-20620/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 15:50:20.944 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 15:50:21.441 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:50:21.441 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:50:21.458 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:50:21.458 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:50:21.475 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:50:21.475 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:50:21.492 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:50:21.492 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:50:21.786 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 15:50:21.786 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 15:50:21.787 20620-20620/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 15:50:21.787 20620-20620/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:结果和在B区域触摸结果类似

1、按下时,Activity使用dispatchTouchEvent方法将事件分发给ViewGroup

2、ViewGroup使用dispatchTouchEvent方法默认将事件分发给自己的onInterceptTouchEvent方法,onInterceptTouchEvent方法默认不拦截,将事件传递给其中的View

3、View的dispatchTouchEvent方法默认将事件分发给自己的onTouchEvent方法,onTouchEvent返回默认super.onTouchEvent(event),将事件回传给ViewGroup的onTouchEvent方法

4、ViewGroup的onTouchEvent也默认将事件传递给Activity的onTouchEvent方法

5、按下事件只好由Activity的onTouchEvent方法自己处理

6、手指移动,Activity已经知道其中的ViewGroup及View都不处理触摸事件,所以使用自己的dispatchTouchEvent方法将移动事件传递给自己的onTouchEvent方法消费

7、手指抬起,同第6步一样,自己分发自己处理。

2、改写ViewGroup的dispatchTouchEvent方法,返回false

  • 1、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    08-10 16:22:05.686 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 16:22:05.686 3933-3933/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 16:22:05.686 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 16:22:07.268 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:22:07.268 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:22:07.352 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:22:07.352 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:22:07.436 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:22:07.436 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:22:07.486 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:22:07.486 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:22:07.873 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:22:07.873 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:22:07.890 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:22:07.890 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:22:07.892 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 16:22:07.892 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:

1、按下时,Activity使用dispatchTouchEvent将事件分发给ViewGroup

2、因为ViewGroup的dispatchTouchEvent返回false,即事件不再继续向下分发,事件被返回给Activity的onTouchEvent进行消费

3、手指移动:Activity既然使用dispatchTouchEvent将事件分发,但从按下事件的反馈中,Activity已经得知ViewGroup不再分发事件,并且将事件返回,所以Activity还是自己用onTouchEvent将事件消费了

4、手指抬起:同手指移动一样,自己分发自己消费

  • 2、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    08-10 16:31:17.409 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 16:31:17.409 3933-3933/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 16:31:17.409 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 16:31:17.912 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:31:17.912 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:31:17.929 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:31:17.929 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:31:17.963 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:31:17.963 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 16:31:18.194 3933-3933/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 16:31:18.194 3933-3933/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:

1、同触摸B区域一的结果,说明ViewGroupd的ispatchTouchEvent返回false,即不再分发事件,其中任何View得不到触摸事件

3、改写ViewGroup的dispatchTouchEvent方法,返回true

  • 1、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    08-10 16:43:37.080 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 16:43:37.080 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 16:43:37.226 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.226 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.243 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.243 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.260 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.411 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.444 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.445 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.464 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 16:43:37.464 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 1

分析:
1、按下时,Activity使用dispatchTouchEvent方法将事件分发;
2、ViewGroup接收到事件,但是ViewGroup的dispatchTouchEvent返回true,即ViewGroup的dispatchTouchEvent将事件消费,不再分发。
3、手指移动、手指抬起:同按下状态一下,Activity使用dispatchTouchEvent分发的事件被ViewGroup的dispatchTouchEvent方法消费了。

  • 2、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    08-10 16:43:37.080 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 16:43:37.080 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 16:43:37.226 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.226 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.243 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.243 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.260 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.411 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.444 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 16:43:37.445 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 16:43:37.464 3206-3206/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 16:43:37.464 3206-3206/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 1

分析:View接收不到任何事件,因为ViewGroup的dispatchTouchEvent方法返回true,事件被ViewGroup的dispatchTouchEvent方法消费掉,不再向下传递了

4、改写ViewGroup的onInterceptTouchEvent方法,返回true

  • 1、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    08-10 17:01:03.576 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:01:03.576 16009-16009/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:01:03.576 16009-16009/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:01:03.576 16009-16009/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:01:03.576 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 17:01:04.148 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:01:04.148 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:01:04.165 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:01:04.165 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:01:04.448 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:01:04.448 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:01:04.448 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:01:04.448 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:Activity的dispatchTouchEvent将事件分发给ViewGroup
2、ViewGroup默认的dispatchTouchEvent方法将事件分发,传递给onInterceptTouchEvent方法,onInterceptTouchEvent方法返回true,将事件拦截,供给自己的onTouchEvent方法消费,
3、ViewGroup的onTouchEvent方法默认不消费,将事件返回给Activity的onTouchEvent方法,最终按下事件由Activity的onTouchEvent自己消费
4、手指移动:Activity辛辛苦苦分发的按下事件,ViewGroup不领情,那么这次移动事件,Activity使用dispatchTouchEvent将事件分发给自己的onTouchEvent,自己消费掉
5、手指抬起:同手指移动一样,Activity自己分发自己消费

  • 2、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    08-10 17:06:27.835 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:06:27.835 16009-16009/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:06:27.835 16009-16009/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:06:27.835 16009-16009/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:06:27.835 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 17:06:27.998 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:06:27.999 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:06:28.015 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:06:28.015 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:06:28.300 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:06:28.300 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:06:28.357 16009-16009/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:06:28.357 16009-16009/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:可以看出,和B区域事件一样,ViewGroup的onInterceptTouchEvent将事件拦截后自己又不消费,返回给Activity,并且ViewGroup中的View接受不到事件消息,因为ViewGroup已经拦截了事件。

5、改写ViewGroup的onInterceptTouchEvent方法,返回false

  • 1、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    08-10 17:13:47.725 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:13:47.725 19111-19111/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:13:47.725 19111-19111/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:13:47.725 19111-19111/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:13:47.725 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 17:13:47.786 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:13:47.786 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:13:47.803 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:13:47.803 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:13:48.005 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:13:48.005 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:13:48.009 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:13:48.009 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:Activity的dispatchTouchEvent将事件分发给ViewGroup
2、ViewGroup默认的dispatchTouchEvent方法将事件分发,传递给onInterceptTouchEvent方法,onInterceptTouchEvent方法返回false,不再拦截,但是ViewGroup触摸区域没有View,所以事件传递给自己onTouchEvent方法
3、ViewGroup的onTouchEvent方法默认不消费,将事件返回给Activity的onTouchEvent方法,最终按下事件由Activity的onTouchEvent自己消费
4、手指移动:Activity辛辛苦苦分发的按下事件,ViewGroup不领情,那么这次移动事件,Activity使用dispatchTouchEvent将事件分发给自己的onTouchEvent,自己消费掉
5、手指抬起:同手指移动一样,Activity自己分发自己消费

  • 2、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    08-10 17:20:04.767 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:20:04.767 19111-19111/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:20:04.767 19111-19111/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:20:04.767 19111-19111/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 17:20:04.767 19111-19111/com.ddz.lifestyle E/View---------onTouchEvent-------: 0
    08-10 17:20:04.768 19111-19111/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:20:04.768 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 17:20:04.896 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:20:04.896 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:20:05.047 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:20:05.047 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:20:05.064 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:20:05.064 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:20:05.081 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:20:05.081 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:20:05.095 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:20:05.095 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:20:05.095 19111-19111/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:20:05.095 19111-19111/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:这次流程依然是Activity的dispatchTouchEvent分发事件给ViewGroup
2、VIewGroup的dispatchTouchEvent方法返回默认的super.dispatchTouchEvent(ev),所以事件继续分发,传递给onInterceptTouchEvent;
3、这时候onInterceptTouchEvent已经返回false,不再拦截,事件终于被传递给ViewGroup中的View了;
4、View接受到事件之后,dispatchTouchEvent方法也是返回默认super.dispatchTouchEvent(ev),事件传递给自己的onTouchEvent方法
5、View的onTouchEvent方法不去消费,事件又被返回给ViewGroup的onTouchEvent方法;
6、但是ViewGroup的onTouchEvent方法也不消费,返回给Activity的onTouchEvent方法
7、Activity的onTouchEvent方法自己消费按下事件(Activity:都不消费,下次不给你传了);
8、Activity说到做到,手指移动和手指抬起事件:Activity使用dispatchTouchEvent方法将事件分发给自己的onTouchEvent方法消费,自己发给自己消费,(再也不给下面的ViewGroup和View这两个什么都不干的人,哈哈哈)

6、改写ViewGroup的onTouchEvent方法,返回true

  • 1、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    08-10 17:32:42.158 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:32:42.158 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:32:42.158 12826-12826/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:32:42.158 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:32:42.194 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:32:42.194 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 17:32:42.194 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 2
    08-10 17:32:42.227 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:32:42.227 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 17:32:42.227 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 2
    08-10 17:32:42.428 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:32:42.428 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 17:32:42.428 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 2
    08-10 17:32:42.442 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:32:42.442 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 1
    08-10 17:32:42.442 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 1

分析:1、手指按下:Activity的dispatchTouchEvent将事件分发给ViewGroup
2、ViewGroup默认的dispatchTouchEvent方法将事件分发,传递给onInterceptTouchEvent方法
3、ViewGroup的onInterceptTouchEvent方法默认返回super.onInterceptTouchEvent(ev),不拦截,但是ViewGroup触摸区域没有View,所以事件传递给自己onTouchEvent方法
3、ViewGroup的onTouchEvent方法返回true, 将按下事件消费掉了,不再返回
4、手指移动:Activity的dispatchTouchEvent方法依然分发事件给ViewGroup;
5、ViewGroup在手指按下事件中已经知道了自己的onInterceptTouchEvent方法默认不拦截,所以移动事件就不再问onInterceptTouchEvent方法了,直接传递给onTouchEvent方法
6、ViewGroup的onTouchEvent方法将事件消费
7、手指提起:同手指移动一模一样的流程,最终事件由ViewGroup的onTouchEvent方法消费掉

  • 2、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    08-10 17:39:21.134 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:39:21.135 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:39:21.135 12826-12826/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:39:21.135 12826-12826/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 17:39:21.135 12826-12826/com.ddz.lifestyle E/View---------onTouchEvent-------: 0
    08-10 17:39:21.140 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:39:21.261 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:39:21.261 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 17:39:21.261 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 2
    08-10 17:39:21.277 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:39:21.278 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 17:39:21.278 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 2
    08-10 17:39:21.428 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:39:21.428 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 17:39:21.428 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 2
    08-10 17:39:21.435 12826-12826/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:39:21.435 12826-12826/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 1
    08-10 17:39:21.435 12826-12826/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 1

分析:
1、手指按下:Activity的dispatchTouchEvent将事件分发给ViewGroup
2、ViewGroup默认的dispatchTouchEvent方法将事件分发,传递给onInterceptTouchEvent方法
3、ViewGroup的onInterceptTouchEvent方法默认返回super.onInterceptTouchEvent(ev),不拦截,这时候ViewGroup中有了View,事件传递给View的dispatchTouchEvent
4、View的dispatchTouchEvent将事件分发给自己的onTouchEvent方法
5、View的onTouchEvent不想消费事件,将事件返回给ViewGroup的onTouchEvent方法
6、ViewGroup的onTouchEvent方法返回true,将事件拿来自己消费掉了,按下事件结束了
7、手指移动:Activity的dispatchTouchEvent方法依然分发事件给ViewGroup;
8、ViewGroup在手指按下事件中已经知道了自己的onInterceptTouchEvent方法默认不拦截,所以移动事件就不再问onInterceptTouchEvent方法了,而且按下事件的时候,ViewGroup中的View不消费事件,所以这次也不再传递给View了,直接传递给ViewGroup的onTouchEvent
9、ViewGroup的onTouchEvent方法返回true,所以消费了移动事件
10、手指抬起同手指移动一样

7、改写ViewGroup的onTouchEvent方法,返回false

  • 1、触摸B区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    08-10 17:49:17.141 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:49:17.142 29978-29978/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:49:17.142 29978-29978/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:49:17.142 29978-29978/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:49:17.142 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 17:49:17.245 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:49:17.245 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:49:17.295 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:49:17.295 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:49:17.409 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:49:17.409 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:49:17.409 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:49:17.409 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:Activity的dispatchTouchEvent将事件分发给ViewGroup
2、ViewGroup默认的dispatchTouchEvent方法将事件分发,传递给onInterceptTouchEvent方法;
3、ViewGroup的onInterceptTouchEvent方法返回默认的super.onInterceptTouchEvent(ev),即不拦截,但是ViewGroup触摸区域没有View,所以事件传递给自己onTouchEvent方法
3、ViewGroup的onTouchEvent方法返回false,不消费事件,将事件返回给Activity的onTouchEvent方法,最终按下事件由Activity的onTouchEvent自己消费
4、手指移动:Activity辛辛苦苦分发的按下事件,ViewGroup不领情,那么这次移动事件,Activity使用dispatchTouchEvent将事件分发给自己的onTouchEvent,自己消费掉
5、手指抬起:同手指移动一样,Activity自己分发自己消费

  • 2、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    08-10 17:52:32.229 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 17:52:32.230 29978-29978/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 17:52:32.230 29978-29978/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 17:52:32.230 29978-29978/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 17:52:32.230 29978-29978/com.ddz.lifestyle E/View---------onTouchEvent-------: 0
    08-10 17:52:32.230 29978-29978/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 17:52:32.230 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 17:52:32.331 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:52:32.331 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:52:32.348 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:52:32.348 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:52:32.735 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 17:52:32.735 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 17:52:32.740 29978-29978/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 17:52:32.741 29978-29978/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:这次流程依然是Activity的dispatchTouchEvent分发事件给ViewGroup
2、VIewGroup的dispatchTouchEvent方法返回默认的super.dispatchTouchEvent(ev),所以事件继续分发,传递给onInterceptTouchEvent;
3、这时候onInterceptTouchEvent也默认不拦截,事件终于被传递给ViewGroup中的View了;
4、View接受到事件之后,dispatchTouchEvent方法也是返回默认super.dispatchTouchEvent(ev),事件传递给自己的onTouchEvent方法
5、View的onTouchEvent方法默认不消费,事件又被返回给ViewGroup的onTouchEvent方法;
6、但是ViewGroup的onTouchEvent方法返回false,不消费,事件又返回给Activity的onTouchEvent方法
7、Activity的onTouchEvent方法自己消费按下事件(Activity:都不消费,下次不给你传了);
8、Activity说到做到,手指移动和手指抬起事件:Activity使用dispatchTouchEvent方法将事件分发给自己的onTouchEvent方法消费,自己发给自己消费,(再也不给下面的ViewGroup和View这两个什么都不干的人,哈哈哈)

8、改写View的dispatchTouchEvent方法,返回false

  • 1、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    08-10 19:20:06.394 29197-29197/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 19:20:06.394 29197-29197/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 19:20:06.394 29197-29197/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 19:20:06.394 29197-29197/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 19:20:06.394 29197-29197/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 19:20:06.395 29197-29197/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 19:20:06.529 29197-29197/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 19:20:06.529 29197-29197/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 19:20:06.563 29197-29197/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 19:20:06.563 29197-29197/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 19:20:06.579 29197-29197/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 19:20:06.580 29197-29197/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 19:20:06.634 29197-29197/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 19:20:06.634 29197-29197/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:Activity的dispatchTouchEvent分发事件给ViewGroup;
2、VIewGroup的dispatchTouchEvent方法返回默认的super.dispatchTouchEvent(ev),所以事件继续分发,传递给onInterceptTouchEvent;
3、ViewGroup的onInterceptTouchEvent也默认不拦截,事件终于被传递给ViewGroup中的View了;
4、View接受到事件之后,dispatchTouchEvent方法返回false,事件不再分发,返回给ViewGroup的onTouchEvent;
5、ViewGroup的onTouchEvent方法默认也不消费,事件再次返回给Activity的onTouchEvent
6、最终,按下事件由Activity的onTouchEvent方法消费掉。
7、由按下事件可知:事件从View–>ViewGroup–>View,事件最终返回给Activity,所以手指移动、抬起事件,不会再继续向下分发,直接有Activity的dispatchTouchEvent传递给自己的onTouchEvent事件消费

9、改写View的dispatchTouchEvent方法,返回true

  • 1、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    08-10 19:29:34.275 22834-22834/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 19:29:34.275 22834-22834/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 19:29:34.275 22834-22834/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 19:29:34.275 22834-22834/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 19:29:34.621 22834-22834/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 19:29:34.621 22834-22834/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 19:29:34.621 22834-22834/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 2
    08-10 19:29:34.621 22834-22834/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 2
    08-10 19:29:34.637 22834-22834/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 19:29:34.637 22834-22834/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-10 19:29:34.637 22834-22834/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 2
    08-10 19:29:34.638 22834-22834/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 2
    08-10 19:29:34.642 22834-22834/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 19:29:34.642 22834-22834/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 1
    08-10 19:29:34.642 22834-22834/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 1
    08-10 19:29:34.642 22834-22834/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 1

分析:
1、手指按下:Activity的dispatchTouchEvent分发事件给ViewGroup;
2、VIewGroup的dispatchTouchEvent方法返回默认的super.dispatchTouchEvent(ev),所以事件继续分发,传递给onInterceptTouchEvent;
3、ViewGroup的onInterceptTouchEvent默认不拦截,事件终于被传递给ViewGroup中的View了;
4、View接受到事件之后,dispatchTouchEvent方法返回true,事件不再分发,直接被View的dispatchTouchEvent消费,不再传递
5、手指移动、手指抬起事件同上,全部被View的dispatchTouchEvent方法消费

10、改写View的onTouchEvent方法,返回false

  • 1、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/View---------onTouchEvent-------: 0
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/ViewGroup-------onTouchEvent-------: 0
    08-10 21:18:33.082 8039-8039/com.ddz.lifestyle E/activity-------onTouchEvent-------: 0
    08-10 21:18:33.108 8039-8039/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 21:18:33.108 8039-8039/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 21:18:33.125 8039-8039/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 21:18:33.125 8039-8039/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 21:18:33.141 8039-8039/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-10 21:18:33.141 8039-8039/com.ddz.lifestyle E/activity-------onTouchEvent-------: 2
    08-10 21:18:33.248 8039-8039/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-10 21:18:33.248 8039-8039/com.ddz.lifestyle E/activity-------onTouchEvent-------: 1

分析:
1、手指按下:这次流程依然是Activity的dispatchTouchEvent分发事件给ViewGroup
2、VIewGroup的dispatchTouchEvent方法返回默认的super.dispatchTouchEvent(ev),所以事件继续分发,传递给onInterceptTouchEvent;
3、这时候onInterceptTouchEvent也默认不拦截,事件终于被传递给ViewGroup中的View了;
4、View接受到事件之后,dispatchTouchEvent方法也是返回默认super.dispatchTouchEvent(ev),事件传递给自己的onTouchEvent方法
5、View的onTouchEvent方法返回false,不消费事件,事件又被返回给ViewGroup的onTouchEvent方法;
6、但是ViewGroup的onTouchEvent方法默认不消费,事件又返回给Activity的onTouchEvent方法
7、最后Activity的onTouchEvent方法自己消费按下事件(Activity:都不消费,下次不给你传了);
8、手指移动:Activity已经知道ViewGroup和View都不消费事件,所以手指移动事件就自己使用dispatchTouchEvent传递给自己的onTouchEvent方法消费掉
9、手指抬起:同手指移动

11、改写View的onTouchEvent方法,返回true

  • 1、触摸C区域(按下、移动、抬起)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    08-11 13:33:27.684 11900-11900/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 0
    08-11 13:33:27.684 11900-11900/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 0
    08-11 13:33:27.684 11900-11900/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 0
    08-11 13:33:27.684 11900-11900/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 0
    08-11 13:33:27.685 11900-11900/com.ddz.lifestyle E/View---------onTouchEvent-------: 0
    08-11 13:33:27.761 11900-11900/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-11 13:33:27.761 11900-11900/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-11 13:33:27.761 11900-11900/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 2
    08-11 13:33:27.761 11900-11900/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 2
    08-11 13:33:27.761 11900-11900/com.ddz.lifestyle E/View---------onTouchEvent-------: 2
    08-11 13:33:27.962 11900-11900/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 2
    08-11 13:33:27.962 11900-11900/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 2
    08-11 13:33:27.962 11900-11900/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 2
    08-11 13:33:27.962 11900-11900/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 2
    08-11 13:33:27.962 11900-11900/com.ddz.lifestyle E/View---------onTouchEvent-------: 2
    08-11 13:33:27.993 11900-11900/com.ddz.lifestyle E/activity-------dispatchTouchEvent-------: 1
    08-11 13:33:27.994 11900-11900/com.ddz.lifestyle E/ViewGroup-------dispatchTouchEvent-------: 1
    08-11 13:33:27.994 11900-11900/com.ddz.lifestyle E/ViewGroup-------onInterceptTouchEvent-------: 1
    08-11 13:33:27.994 11900-11900/com.ddz.lifestyle E/View---------dispatchTouchEvent-------: 1
    08-11 13:33:27.994 11900-11900/com.ddz.lifestyle E/View---------onTouchEvent-------: 1

分析:
1、手指按下:这次流程依然是Activity的dispatchTouchEvent分发事件给ViewGroup
2、VIewGroup的dispatchTouchEvent方法返回默认的super.dispatchTouchEvent(ev),所以事件继续分发,传递给onInterceptTouchEvent;
3、这时候onInterceptTouchEvent也默认不拦截,事件终于被传递给ViewGroup中的View了;
4、View接受到事件之后,dispatchTouchEvent方法也是返回默认super.dispatchTouchEvent(ev),事件传递给自己的onTouchEvent方法
5、View的onTouchEvent方法返回true,消费事件按下事件结束
6、手指移动:Activity已经知道ViewGroup中的View的onTouchEvent方法要消费事件,手指移动事件就重复按下事件的流程,一直传递给View的onTouchEvent方法,直到事件被消费;
7、手指抬起:同手指移动一样

###总结

####ViewGroup:
dispatchTouchEvent 分发
ViewGroup接收到事件之后,根据dispatchTouchEvent决定是否分发下去

  • 1、默认返回 super.dispatchTouchEvent(ev)方法,即默认分发事件
  • 2、如果返回false;事件将不再分发,直接返回给上一层的onTouch方法,并且后面的事件将不再分发给当前ViewGroup,上层直接自己分发并消费掉
  • 3、如果返回true,事件将不再分发并且由ViewGroup的dispatchTouchEvent 消费掉,后面的触摸事件也会同样被ViewGroup的dispatchTouchEvent 消费

onInterceptTouchEvent 拦截
ViewGroup的dispatchTouchEvent如果默认将事件分发下去,传递给onInterceptTouchEvent方法,由onInterceptTouchEvent方法决定是否拦截

  • 1、默认返回super.onInterceptTouchEvent(ev) 即不拦截,事件继续传递
  • 2、改为返回true,事件直接传递给ViewGroup的onTouchEvent方法消费,不再传递给ViewGroup中的View
  • 3、如果返回false,事件将传递给ViewGroup中的View去处理

onTouchEvent 消费
如果ViewGroup中的onInterceptTouchEvent 默认不拦截事件,这时根据ViewGroup的onTouchEvent返回值来判断

  • 1、默认返回super.onTouchEvent(event),事件将传递给ViewGroup中的View进行处理,如果ViewGroup中View不消费事件,事件将会返回给ViewGroup的onTouchEvent方法处理,ViewGroup的onTouchEvent方法默认不处理,返回给上层的onTouchEvent方法处理
  • 2 、如果返回true,,事件也会传递给ViewGroup中的View进行处理,如果ViewGroup中View不消费事件,事件将会返回给ViewGroup的onTouchEvent方法,这时ViewGroup的onTouchEvent直接将事件消费掉,不返回上层的onTouchEvent方法了
  • 3、如果返回false,事件将默认由ViewGroup传递给View,View不处理又返回给ViewGroup的onTouchEvent,ViewGroup的onTouchEvent返回false,所以事件又返回给上层的onTouchEvent方法

####View:
dispatchTouchEvent 分发
View接收到事件后,根据dispatchTouchEvent方法返回值,判断是否继续分发

  • 1、默认返回super.dispatchTouchEvent(ev),事件将分发下去,传递给View自己的onTouchEvent进行处理,
  • 2、如果返回false,事件将不分发,直接返回给上层ViewGroup的onTouchEvent方法进行处理
  • 3、如果返回true,事件也不再分发,直接由View的dispatchTouchEvent 进行消费,并且以后的事件同样直接被View的dispatchTouchEvent 方法消费了

onTouchEvent 消费
如果View的dispatchTouchEvent 将事件传递给onTouchEvent方法,将根据onTouchEvent方法的返回值决定是否消费事件

  • 1、默认返回super.onTouchEvent(event),默认不消费事件,事件将返回给上层ViewGroup的onTouchEvent方法处理
  • 2、如果改为true,将事件消费,不再返回,以后其他事件同样被onTouchEvent方法消费
  • 3 、如果改为false,同默认方法一样,不再消费,返回给上层处理

所以,触摸事件的所有流程已经很清晰了

对于事件分发:(dispatchTouchEvent)
如果想事件不向下传递,自己消费掉 :将当前的dispatchTouchEvent返回true;
如果想事件不向下传递,返回给上层 :将当前的dispatchTouchEvent返回false;
对于事件拦截:(onInterceptTouchEvent)
如果想拦截事件,给自己的onTouchEvent方法消费 :将onInterceptTouchEvent返回true
如果不拦截事件,默认向下传递 :将onInterceptTouchEvent返回false或者返回默认值
对于事件消费:(onTouchEvent)
如果不想消费,返回给上层 :将onTouchEvent返回默认或者返回false;
如果想消费,不再返回 :将onTouchEvent返回true;

|