1. View的位置参数
1. Android坐标系
在Android中,屏幕左上角的顶点是Android坐标系的原点(0, 0),x轴和y轴的正方向分别为向右和向下。
2. View的位置参数
1. View的四个顶点
View的位置由它的四个顶点决定,分别对应View的四个属性:left、top、right、bottom。其中:
- left:左上角的x坐标
- top:左上角的y坐标
- right:右下角的x坐标
- bottom:右下角的y坐标
这些坐标相对于View的父容器,因此是一种相对坐标。
获取这些属性的方法如下:
getTop():获取View顶边到其父布局顶边的距离getLeft():获取View左边到其父布局左边的距离getRight():获取View右边到其父布局左边的距离getBottom():获取View底边到其父布局顶边的距离
2. 其它参数
从Android 3.0开始,View增加了以下几个参数,并为其提供了get/set方法:
getX():View相对于父容器左上角的x坐标getY():View相对于父容器左上角的y坐标getTranslationX():View左上角相对于父容器的x坐标偏移量,默认值为0getTranslationY():View左上角相对于父容器的y坐标偏移量,默认值为0
上述参数之间的换算关系如下:
x = left + translationX
y = top + translationY
需要注意的是,View在平移过程中,top和left表示的是原始左上角的位置,它们的值不会发生改变,而x、y、translationX和translationY这四个参数会改变。
2. MotionEvent
当我们点击屏幕时,会产生点击事件,这个点击事件被封装成一个类:MotionEvent。典型的事件类型有:
- ACTION_DOWN:手指刚接触屏幕
- ACTION_MOVE:手指在屏幕上移动
- ACTION_UP:手指从屏幕上松开的一瞬间
一次手指触摸屏幕的行为会触发一系列点击事件,如:
- 点击屏幕后立即松开,事件序列为
DOWN -> UP - 点击屏幕滑动一会再松开,事件序列为
DOWN -> MOVE -> ... -> MOVE -> UP
通过MotionEvent对象,我们可以得到点击事件发生的坐标:
getX()/getY():返回相对于当前View左上角的x和y坐标getRawX()/getRawY():返回相对于手机屏幕左上角的x和y坐标
3. TouchSlop
TouchSlop是系统识别出的被认为是滑动的最小距离,这是一个常量,设备之间可能不同。当滑动距离小于这个常量时,系统不会认为是在滑动。获取TouchSlop常量的方法如下:
int touchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
这个常量的意义在于处理滑动时,可以利用它进行过滤。例如,当滑动距离小于这个值时,可以认为未达到滑动的临界值,从而不是滑动。这样可以提升用户体验。在源码中,可以在frameworks/base/core/res/res/values/config.xml文件中找到这个常量的定义:
<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
4. VelocityTracker
VelocityTracker用于追踪手指在滑动过程中的速度,包括水平和垂直方向的速度。使用它的过程如下:
首先,在View的onTouchEvent方法中追踪当前事件的速度:
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
然后,可以获取当前滑动速度:
// 设置速度计算的时间间隔,例1000ms
velocityTracker.computeCurrentVelocity(1000);
// 获取水平方向滑动速度,单位:像素/1000ms
int xVelocity = (int) velocityTracker.getXVelocity();
// 获取垂直方向滑动速度,单位:像素/1000ms
int yVelocity = (int) velocityTracker.getYVelocity();
注意:
- 获取速度之前必须先计算速度,即调用
getXVelocity和getYVelocity之前必须调用computeCurrentVelocity方法。 - 速度可以为负数,当手指从右向左滑动时,水平方向速度为负值。
速度计算公式:
速度 = (终点位置 - 起点位置) / 时间段
computeCurrentVelocity方法的参数表示时间间隔,单位是毫秒(ms)。
例如,通过velocityTracker.computeCurrentVelocity(100);获取速度,那么得到的速度就是手指在100ms内滑过的像素数。
最后,不再使用VelocityTracker时,需要调用clear方法重置并回收内存:
velocityTracker.clear();
velocityTracker.recycle();
5. GestureDetector
GestureDetector用于检测用户的单击、滑动、长按、双击等手势行为。使用步骤如下:
首先,创建GestureDetector对象并实现onGestureListener接口,如有需要,还可以实现onDoubleTapListener监听双击行为:
GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.OnGestureListener() {
// 实现所需的方法
});
gestureDetector.setIsLongpressEnabled(false); // 解决长按屏幕后无法拖动的问题
然后,在目标View的onTouchEvent方法中处理触摸事件:
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
onGestureListener和onDoubleTapListener接口方法如下:
| 方法名 | 描述 | 所属接口 |
|---|---|---|
onDown | 手指轻轻触摸屏幕的一瞬间,由1个ACTION_DOWN触发 | onGestureListener |
onShowPress | 手指轻轻触摸屏幕,尚未松开或拖动,由1个ACTION_DOWN触发。强调没有松开或拖动的状态 | onGestureListener |
onSingleTapUp | 手指轻轻触摸屏幕后松开,伴随着1个ACTION_UP触发,这是单击行为 | onGestureListener |
onScroll | 手指按下屏幕并拖动,由1个ACTION_DOWN和多个ACTION_MOVE触发,这是拖动行为 | onGestureListener |
onLongPress | 用户长久按着屏幕不放,即长按 | onGestureListener |
onFling | 用户按下触摸屏、快速滑动后松开,由1个ACTION_DOWN、多个ACTION_MOVE和1个ACTION_UP触发,这是快速滑动行为 | onGestureListener |
onDoubleTap | 双击,由2次连续单击组成,不可能与onSingleTapConfirmed共存 | onDoubleTapListener |
onSingleTapConfirmed | 严格单击行为,与onSingleTapUp区别在于,后者可能是双击中的一次单击,而前者不可能紧跟另一个单击 | onDoubleTapListener |
onDoubleTapEvent | 双击行为期间,ACTION_DOWN、ACTION_MOVE和ACTION_UP都会触发此回调 | onDoubleTapListener |
常用方法包括:onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)、onDoubleTap(双击)。
6. Scroller
Scroller用于实现View的弹性滑动,避免View使用scrollTo/scrollBy方法瞬间滑动,提供平滑过渡效果。Scroller与View的computeScroll方法配合使用。
实现步骤如下:
- 定义平滑滑动方法:
public void smoothScrollTo(int destX, int destY){
int scrollX = getScrollX();
int delta = destX - scrollX;
// startScroll保存滑动参数,滑动效果由invalidate方法触发
mScroller.startScroll(scrollX, 0, delta, 0, 1000);
// invalidate方法导致View重绘,间接调用computeScroll方法
invalidate();
}
- 重写
computeScroll方法完成滑动:
@Override
public void computeScroll() {
// 计算当前滑动位置
if (mScroller.computeScrollOffset()){
// 滑动View到当前计算位置
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
// 继续调用computeScroll方法,完成整个滑动过程
postInvalidate();
}
}
scrollTo方法瞬间滑动View,而computeScroll方法根据时间流逝逐步完成滑动,使滑动过程更加平滑自然。