一、什么是View
在开始深入了解View的事件体系之前,我们必须要了解到底什么是View。View是Android中所有控件的基类,TextView、Button、LinearLayout等控件都继承自View,这些View既可以是一个独立的控件,也可以是一个可以包含多个子View的控件组(ViewGroup),而且View与View之间是可以有嵌套关系的。例如比较常见的ViewGroup控件,它本身既是一个View,又可以包含多个子View。明白View的这种层级关系有助于理解View的工作机制。
接下来几节我们分析一些View中比较常用的基础概念。
二、View的位置参数
View的位置由它的四个顶点共同决定,分别对应View的四个属性:
- top 左上角纵坐标
- left 左上角横坐标
- right 右下角横坐标
- bottom 右下角纵坐标
也就是说,View的左上角坐标为(left,top),右下角坐标为(right,bottom)。如果想获取这些属性值,只需要调用 getTop()/getLeft()/getRight()/getBottom() 即可。
需要注意的是,这些坐标都是相对View的父容器来说的,是相对坐标。

如图所示,我们可以很容易计算出:
width = right - left
height = bottom - top
Android3.0开始,增加了x、y、translationX和translationY,这几个参数也是相对父容器的坐标。(x、y)是View的左上角的坐标;translationX和translationY是View的左上角相对于父容器的偏移量,默认都为0。
这些参数的换算关系如下:
x = left + translationX
y = right + translationY
需要注意的是,View在平移过程中,发生变化的是x、y、translationX和translationY这四个参数,top和left表示的是左上角的原始坐标,其值是不会发生变化的。
三、MotionEvent和TouchSlop
MotionEvent
手指接触屏幕后会产生一系列事件,典型的事件类型
- ACTION_DOWN 手指刚接触屏幕
- ACTION_UP 手指从屏幕上拿开
- ACTION_MOVE 手指在屏幕上移动
点击屏幕后直接松开,事件序列为ACTION_DOWN ->ACTION_UP
点击屏幕滑动一会再松开,事件序列为ACTION_DOWN -> ACTION_MOVE …… ACTION_MOVE -> ACTION_UP
除了以上提到的内容,我们还可以通过MotionEvent对象获取到点击事件的x、y坐标。系统提供了两组方法:
- getX()/getY() 相对于当前View的左上角坐标
- getRawX()/getRawY() 相对于手机屏幕左上角坐标
TouchSlop
TouchSlop是系统能识别出的被认为是滑动的最小距离,如果滑动的距离小于这个值,那么系统就不认为你是在进行滑动操作。这个值是一个常量,与设备有关,我们通常可以使用这个常量过滤一些我们认为没有意义的操作。这个常量的获取方式:ViewConfiguration.get(getContent()).getScaledTouchSlop() 。
四、VelocityTracker、GestureDetector和Scroller
VelocityTracker
速度追踪,用于追踪手指在屏幕上滑动的速度,包括水平速度和竖直速度。使用方法很简单:
- VelocityTracker.obtain()获取VelocityTracker 实例;
- velocityTracker.addMovement(event)把当前事件的event添加到velocityTracker里;
- 调用velocityTracker.computeCurrentVelocity(int units)设置速度的计算时间然后开始计算速度
- 最后就可以使用velocityTracker.getXVelocity()获取速度。
这里的速度指的是一段时间内手指滑过的像素数。速度的计算公式很简单:

根据Android系统的坐标系和上面的公式我们可以知道,速度也可以是负值。下面是VelocityTracker 使用示例:
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
//有一点要注意,必须要先执行计算速度的方法computeCurrentVelocity,才能获取到速度
velocityTracker.computeCurrentVelocity(1000);
//获取不同方向的速度
float xVelocity = velocityTracker.getXVelocity();
float yVelocity = velocityTracker.getYVelocity();
最后,当我们不再使用它的时候,我们需要调用它的clear或者recycle来重置并回收内存。
if (velocityTracker != null) {
velocityTracker.clear();
//or
velocityTracker.recycle();
}
GestureDetector
GestureDetector被翻译为手势检测器,它的主要作用是辅助检测用户的单击、双击、长按、滑动等一系列行为,它的使用过程也比较简单。
1. 首先需要创建一个GestureDetector对象,并且实现OnGestureListener接口中的一系列方法
gestureDetector = new GestureDetector(getApplicationContext(), new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});
我们也可以选择性地实现OnDoubleTapListener接口来监听双击行为
gestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
});
2. 接着我们就可以使用gestureDetector对象接管目标View的onTouchEvent方法
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
至此,手势检测已经准备就绪了,接下来的工作就是根据需要重写OnGestureListener和OnDoubleTapListener接口内的方法,所以我们必须要了解各个方法的含义。


如果只是监听滑动相关的事件,我们可以自己在onTouchEvent中实现,如果要监听双击这种行为,使用GestureDetector会比较合适。 ——《Android开发艺术探索》作者给读者的建议
Scroller
Scroller是弹性滑动对象,用于完成View的弹性滑动。一般情况,我们可以直接使用View的scrollTo和scrollBy方法来进行滑动,但是这种滑动是瞬间完成的,没有过渡效果。但是Scroller不同,它在实现滑动的时候是有过渡效果的,所以用它来实现滑动能提升用户体验。Scroller本身无法让View弹性滑动,需要配合View的computeScroll方法一起使用才能完成这个功能。
下一篇:Android View的事件体系(二)View的滑动
参考
《Android开发艺术探索》
网友评论