Android 开发艺术探索读书笔记(四)
前面用 Editor.md 写好了,本来想关掉 as 发布博客的,结果一不小心把 chrome 关掉了,真是日了狗啊,没办法只能重写了。
第 3 章 View 的事件体系
本章主要介绍 VIew 的相关知识和事件体系。
3.1 View 基础知识
本节主要知识点:View 的位置参数、MotionEvent 和 TouchSlop 对象、VelocityTracker、GestureDetector 和 Scroller 对象。
View 是 Android 中所有控件的基类,ViewGroup 是 View 的子类,是用来存放其他控件的一种 VIew。View 的位置主要由它的四个顶点决定,分别对应于 View 的四个属性:top、left、right、bottom,这四个属性分别是相对于 View 的父容器的坐标。
如果要知道 View 的位置信息,可以通过调用 View 对象的 getLeft()、getTop()、getRight()、getBottom() 来获得。从 Android 3.0 开始,View 增加了 x、y 参数,用来表示 VIew 左上角的坐标,和 translationX、translationY 参数,用来表示 View 左上角相对于父容器的坐标,View 也为这些参数提供了 get/set 方法,方便我们使用。这几个参数的换算关系如下:
- x = left + translationX
- y = top + translationY
需要注意的是,在 VIew 的平移过程中,top 和 left 表示的是原始左上角的位置信息,其值并不会发生改变。
在手指接触屏幕后会产生一系列事件,如:
- ACTION_DOWN——手指刚接触屏幕
- ACTION_MOVE——手指在屏幕上移动
- ACTION_UP——手指松开
当发生这些事件时,我们可以通过 MotionEvent 对象来获取 View 的坐标信息:
tv_test.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 获取相对于当前 View 左上角坐标
event.getX();
event.getY();
// 获取绝对坐标
event.getRawX();
event.getRawY();
return false;
}
});
TouchSlop 是系统所能识别出的被认为是滑动的最小距离,通过如下方式可以获取这个值:
int value = ViewConfiguration.get(MainActivity.this).getScaledTouchSlop();
VelocityTracker 即速度追踪,用于追踪手指在屏幕上滑动的速度。使用方法如下:
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 创建对象并添加追踪
tracker = VelocityTracker.obtain();
tracker.addMovement(event);
break;
case MotionEvent.ACTION_UP:
// 先计算速度再取出
// 传入时间单元
tracker.computeCurrentVelocity(1000);
// 这两个值可以为负数,比如从右向左滑动时
int xVelocity = (int) tracker.getXVelocity();
int yVelocity = (int) tracker.getYVelocity();
break;
}
return false;
}
最后,如果不需要使用它了,就调用 clear() 方法来重置并回收内存:
tracker.clear();
tracker.recycle();
GestureDetector 就是手势检测,是用来检测用户的单击、滑动、长按、双击等行为的一个对象,使用时首先创建一个对象:
mGestureDetector = new GestureDetector(MainActivity.this, new GestureDetector.OnGestureListener() {
// 手指轻触屏幕一瞬间,由 1 个 ACTION_DOWN 触发
@Override
public boolean onDown(MotionEvent e) {
return false;
}
// 手指轻触屏,尚未松开或滑动,强调的是没有松开或滑动的状态
@Override
public void onShowPress(MotionEvent e) {
}
// 手指松开,伴随着一个 ACTION_UP 而触发,这是一个单击行为
@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;
}
});
// 解决长按屏幕后无法滑动的问题
mGestureDetector.setIsLongpressEnabled(true);
然后在 View 的 onTouch 事件中添加如下代码即可:
@Override
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
Scroller 是一个弹性滑动对象,用于实现 View 的弹性滑动。当使用 View 的 scrollTo/scrollBy 方法进行滑动时,其过程是瞬间完成的,这样没有过渡效果的滑动用户体验不好,如果我们需要一个有过渡效果的滑动,就要用到 Scroller 对象了,它的使用也很简单:
public class MyView extends View {
private Scroller mScroller;
public MyView(Context context) {
super(context);
}
private void init() {
mScroller = new Scroller(getContext());
}
private void smoothScrollTo(int destX, int destY) {
int scrollX = getScrollX();
int delta = destX - scrollX;
// 1000 ms 内滑向 destX
mScroller.startScroll(scrollX, 0, delta, 0, 1000);
invalidate();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
}
至于它为什么能实现弹性滑动,我们将在下一节中详细介绍。