第3章View的事件体系
本章将介绍 Android中十分重要的一个概念:iew,虽然说View不属于四大组件,但是它的作用堪比四大组件,甚至比 Receiver和 Provider的重要性都要大。在 Android开发中, ActivityAndroid承担这可视化的功能,同时系统提供了很多基础控件,常见的有 Button TextView、 CheckBox等。很多时候仅仅使用系统提供的控件是不能满足需求的,因此我们就需要能够根据需求进行新控件的定义,而控件的自定义就需要对 Android的iew体系有深入的理解,只有这样才能写出完美的自定义控件。同时 Android手机属于移动设备,移动设备的一个特点就是用户可以直接通过屏幕来进行一系列操作,一个典型的场景就是屏幕的滑动,用户可以通过滑动来切换到不同的界面很多情况下我们的应用都需要支持滑动操作,当处于不同层级的Vcw都可以响应用户的滑动操作时,就会带来一个问题,那就是滑动冲突。如何解决滑动冲突呢?这对于初学者来说的确是个头疼的问题,其实解决滑动冲突本不难,它需要读者对Vicw的事件分发机制有一定的了解,在这个基础上,我们就可以利于这个特性从而得出滑动冲突的解决方法。上述这些内容就是本章所要介绍的内容,同时,View的内部工作原理和自定义Vew相关的知识会在第4章进行介绍
3.1 view基础知识
本节主要介绍View的一些基础知识,从而为更好地介绍后续的内容做铺垫主要介绍的内容有:
- View的位置参数
- MotionEvent
- TouchSlop
- Tracker
- GestureDetector
- Scroller对象
通过对这些基础知识的介绍,可以方便读者理解更复杂的内容。类似的基础概念还有不少,但是本节所介绍的都是一些比较常用的,其他不常用的基础概念读者可以自行了解。
3.1.1什么是View
在介绍View的基础知识之前,我们首先要知道到底什么是 View View是 Android中所有控件的基类,不管是简单的 Button和 TextView还是复杂的 Relativelayout和 List View它们的共同基类都是View.所以说,View是一种界面层的控件的一种抽象,它代表了一个控件。除了View,还有 ViewGroup,从名字来看,它可以被翻译为控件组,言外之意是 ViewGroup内部包含了许多个控件,即一组Vie。在 Android的设计中, ViewGroup也继承了View,这就意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件,通过这种关系就形成了View树的结构,这和Web前端中的DOM树的概念是相似的。根据这个概念,我们知道, Button显然是个View,而 LinearLayout不但是一个View而且还是一个 ViewGroup,而 ViewGroup内部是可以有子ew,这个子iew同样还可以是 ViewGroup,依此类推。 明白View的这种层级关系有助于理解View的工机制如图3-1所示,可以看到自定义的 TestButton是一View,它继承了 TextView,而 Text View则直接继承了view,因此不管怎么说, TestButton都是一个View,同理我们也可以构造出一个继承自 ViewGroup的控件图3-1 TestButton的层次结构
3.1.2View的位置参数
view的位置主要由它的四个顶点来决定,分别对应于View的四个属性: top、left、right、bottom
- top是上角纵坐标,
- left是左上角横坐标
- right是右下角横坐标,
- bottom是右下角纵坐标
需要注意的是,这些坐标都是相对于Vew的父容器来说的,因此它是一种相对坐标,View的坐标和父容器的关系如图3-2所示在Android中,x轴和y轴的正方向分别为右和下,这点不难理解,不仅仅是 Android,部分显示系统都是按照这个标准来定义坐标系的.
图3-2View的位置坐标和父容器的关系
根据图3-2,我们很容易得出View的宽高和坐标的关系:
width = right -lef
height = bottom top
那么如何得到View的这四个参数呢?也很简单,在View的源码中它们对应于mLeftt mRight、mtop和 mBottom四个成员变量,获取方式如下所示。
- Left=getLeft();
- Right =getRight();
- Top=getTop();
- Bottom =getBottom();
从 Android3.0开始,vicw增加了额外的几个参数:
x、y、translationX、translationY
其中x和y是Vicw左上角的坐标,而 translationX和 translationYview左上角相对父容器的偏移量。这几个参数也是相对于父容器的坐标,且 translationX translationY默认值是0,和Viw的四个基本的位置参数一样View也为它们提供了 get/set方法,这几个参数的换算关系如下所示。
x = left + translationX
y = top + trannlationY
需要注意的是,Vicw在平移的过程中,top和eft表示的是原始左上角的位置信息,其值并不会发生改变,此时发生改变的是x、y、 translationx和 translationY这四个参数.
3.1.3 MotionEvent和 TouchSlop
1.MotionEvent
在手指接触屏幕后所产生的一系列事件中,典型的事件类型有如下几种:
- ACTION DOWN 手指刚接触屏幕;
- ACTION MOVE 手指在屏幕上移动
- ACTION UP 手指从屏幕上松开的一瞬间 正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件,考虑如下几种情况:
- 点击屏幕后离开松开,事件序列为DONUP
- 点击屏幕滑动一会再松开,事件序列为DOWN-MOVE-MOVE-UP
上述三种情况是典型的事件序列,同时通过 MotionEvent对象我们可以得到点击事件发生的x和y坐标为此,系统提供了两组方法:getx/gty和 getRawX/getRawY它们的区别其实很简单,get/gety返回的是相对于当前iew左上角的x和y坐标,而 getRawX/返回的是相对于手机屏幕左上角的x和y坐标。
2.TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离,换句话说,当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为你是在进行滑动操作原因很简单:滑动的距离太短,系统不认为它是滑动这是一个常量,和设备有关,在不设备上这个值可能是不同的,通过如下方式即可获取这个常量: ViewConfiguration.get(getContext()).getScaled TouchSlop().
这个常量有什么意义呢?当我们在处理滑动时,可以利用这个常量来做一些过滤,比如当两次滑动事件的滑动距离小于这个值,我们就可以认为未达到滑 动距离的临界值,因此就可以认为它们不是滑动,这样做可以有更好的用户体验,其实如果细心的话,可以在源码中找到这个常量定义,在 frameworks/basecore/resres/values/config.xml件中,如下所示,这个“config_viewConfigurationTouchSlop对应的就是这个常量的定义。
3.1.4 VelocityTracker GestureDetector和 Scroller
1.VelocityTracker
速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向的速度。它的使用过程很简单,首先,在Vicw的 on TouchEvent方法中追踪当前单击事件的速度:
VelocityTracker = velocitylracker.VelociyTracker.obtain();
velocityTracker.addMovenent(event);
接着,当我们想知道当前的滑动速度时,这个时候可以采用如下方式来获得当前的速度:
velocityTracker.cotputeCurrentvelocity(1000);
int xvelocity = (int) velocityTracker.getXvelocity();
int yvelocity = (int) velocityTracker.getYvelocity();
在这一步中有两点需要注意,第一点,获取速度之前必须先计算速度,即 getXVclocity的速度是指一段时间内手指所滑过的像素数,比如将时间间隔设为1000ms时,在1s内,手指在水平方向从左向右滑过100像素,那么水平速度就是100。注意速度可以为负数,当手指从右往左滑动时,水平方向速度即为负值这个需要理解一下。速度的计算可以用如下公式来表示:
速度 =(终点位置 - 起点位置)/ 时间段
根据上面的公式再加上 Android系统的坐标系,可以知道,手指逆着坐标系的正方向滑动,所产生的速度就为负值,另外, computeCurrentVelocity 这个方法的参数表示的是个时间单元或者说时间间隔,它的单位是毫秒(ms)计算速度时得到的速度就是在这个时间间隔内手指在水平或竖直方向上所滑动的像素数。针对上面的例子,如果我们通过 celocityTracker.comraputeCurentVelocity(100)取速度,那么得到的速度就是手指在100ms内所滑过的像素数,因此水平速度就成了10像素每100ms(这里假设滑动过程是匀速的),即水平速度为10,这点需要好好理解一下.
最后,当不需要使用它的时候,需要调用 clear方法来重置并回收内存
velonstyTracker.lear()
velocityTracker, recyele (
上面就是如何使用 Velocity Tracker对象的全过程,看起来并不复杂
2.GestureDetector
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为要使用 GestureDetector也不复杂,参考如下过程。
首先,需要创建一个GestureDetector象并实现 OnGestureL istener接口,根据需要我们还可以实现 On Double TapListener从而能够监听双击行为:
GestureDetector mGestureDetoctor new GestureDetector(this
/解决长按屏幕后无法拖动的班象
nGestureLetector,= IsLongpressEnabled falae):
接着,接管目标View的 on TouchEvent方法,在待监听View的 on TouchEvent方法中添加如下实现:
boclean consumne -mgestuzel
return consume
做完了上面两步,我们就可以有选择地实现 OnGestureL istener OnDoubleTapListener中的方法了,这两个接口中的方法介绍如表3-1所示。
表3-1 OnGestureListener和 OnDouble TapL istener中的方法介绍
方法名
述
所属接口 onDown
指轻轻触屏幕的一瞬,由个 ACTION DOWN发 nGestureListener onShowPress
指轻轻屏幕,尚未松开或动,个 ACTION DOWN发
. OnGestureListener注意和 onDowa的区别,它强调的是没有松开或者动的状态
MSingleTapUp
手指(轻轻触摸屏幕后)松开,随着1个 MotionEvea ACTIONUP
OnGestureListener触发,这是击行1个ACT
onScroll
手指按下屏幕并,个 ACTION DOWN, ACTION MOVE
CnGestureListener触发,这是推动行为