【朝花夕拾】Android自定义View篇之(十)TouchSlop及VelocityTracker
前言
在Android事件中,有几个比较基本的概念和知识点需要掌握。比如,表示最小移动阈值的TouchSlop,追踪事件速度的VelocityTracker,用于检测手势的GestureDetector,实现View弹性滑动的Scroller,用户帮助处理View和事件的辅助工具类ViewDragView等。这些都是使用事件、理解事件中需要掌握的知识点。本篇将简单介绍Slop和VelocityTracker的基本知识。
一、TouchSlop
TouchSlop是一个系统常量,用于表示系统能够识别的被认为是滑动的最小距离,也就是说当在屏幕上滑动的距离小于这个值时,系统不认为这是滑动操作。这个值和设备有关,手机生产商可以自行设置该值。通过该值,可以过滤掉一些滑动距离太小的操作等,从而提高用户体验。该值保存在文件frameworks/base/core/res/res/values/config.xml中,如下所示:
<!-- Base "touch slop" value used by ViewConfiguration as a movement threshold where scrolling should begin. --> <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
默认情况下,该值一般都是8dp。
Log.i("songzheweiwang", "TouchSlop=" + ViewConfiguration.get(this).getScaledTouchSlop());
打印log为(测试机的density=3.0):
07-15 17:02:22.382 6789-6789/com.example.demos I/songzheweiwang: TouchSlop=24
这里我们顺便看看该方法的源码:
1 //======android.view.ViewConfiguration.java====== 2 private final int mTouchSlop; 3 /** 4 * @return Distance in pixels a touch can wander before we think the user is scrolling 5 */ 6 public int getScaledTouchSlop() { 7 return mTouchSlop; 8 } 9 ...... 10 private ViewConfiguration(Context context) { 11 ...... 12 mTouchSlop = res.getDimensionPixelSize( com.android.internal.R.dimen.config_viewConfigurationTouchSlop); 13 ...... 14 }
二、VelocityTracker
该类用于速度追踪,追踪手指在滑动过程中的速度,包括水平方向速度和竖直方向速度。在前面的文章中介绍滑动冲突的解决方法时,就提到过通过比较水平方向速度和竖直方向速度来判断控件的滑动方向。这里我们简单介绍一下获取速度的方法。
1、初始化
进行初始化,获取VelocityTracker实例,通过如下方法实现:
VelocityTracker mVelocityTracker = VelocityTracker.obtain();
其源码如下:
1 /** 2 * Retrieve a new VelocityTracker object to watch the velocity of a 3 * motion. Be sure to call {@link #recycle} when done. You should 4 * generally only maintain an active object while tracking a movement, 5 * so that the VelocityTracker can be re-used elsewhere. 6 * 7 * @return Returns a new VelocityTracker. 8 */ 9 static public VelocityTracker obtain() { 10 VelocityTracker instance = sPool.acquire(); 11 return (instance != null) ? instance : new VelocityTracker(null); 12 }
可见,VelocityTracker原本有一个“池”,“池”中的实例用完后才会新实例一个。注释中包含了很多信息,什么时候调用等,最好参照这个注释说明来做。
2、添加用户事件
由于要追踪事件的速度,所以需要向VelocityTracker中添加事件。使用如下方式进行添加:
mVelocityTracker.addMovement(event);
源码如下:
1 /** 2 * Add a user's movement to the tracker. You should call this for the 3 * initial {@link MotionEvent#ACTION_DOWN}, the following 4 * {@link MotionEvent#ACTION_MOVE} events that you receive, and the 5 * final {@link MotionEvent#ACTION_UP}. You can, however, call this 6 * for whichever events you desire. 7 * 8 * @param event The MotionEvent you received and would like to track. 9 */ 10 public void addMovement(MotionEvent event) { 11 if (event == null) { 12 throw new IllegalArgumentException("event must not be null"); 13 } 14 nativeAddMovement(mPtr, event); 15 } 16 ...... 17 private static native void nativeAddMovement(long ptr, MotionEvent event);
这方法可以在任何你希望的事件中进行调用。
3、计算速度
在获取速度前必须先计算速度,使用方法如下:
mVelocityTracker.computeCurrentVelocity(int units);
我们在计算速度的时候,都需要指定时间单位,比如km/h,m/s等,表示在单位时间内的运动路程。这里的units单位为ms,得到的速度表示单位时间内移动的像素数目。比如这里参数为1000时,那么后续获得的速度就是,每1000ms移动的像素数。
该方法的源码如下:
1 /** 2 * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum 3 * velocity of Float.MAX_VALUE. 4 * 5 * @see #computeCurrentVelocity(int, float) 6 */ 7 public void computeCurrentVelocity(int units) { 8 nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE); 9 } 10 ...... 11 private static native void nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity);
4、获取速度
前面我们说了,在获取速度前,一定要先计算速度。获取速度通过如下两个方法来完成:
float xVelocity = mVelocityTracker.getXVelocity(); float yVelocity = mVelocityTracker.getYVelocity();
这两行代码分别用于获取水平方向和竖直方向的速度。我们知道,速度是有方向的,以不同的方向为标准,速度就有正负。在这里获取的速度,是以X轴正相反为正,即顺着X轴正方向时速度为正,逆着X轴正方向时速度为负。同样,对于竖直方向,顺着Y轴正方向为正,逆着Y轴正方向为负。前面我们也说过了,这里的速度是指,给定的时间间隔内,手指所滑过的像素数。
源码如下:
1 /** 2 * Retrieve the last computed X velocity. You must first call 3 * {@link #computeCurrentVelocity(int)} before calling this function. 4 * 5 * @return The previously computed X velocity. 6 */ 7 public float getXVelocity() { 8 return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID); 9 } 10 /** 11 * Retrieve the last computed Y velocity. You must first call 12 * {@link #computeCurrentVelocity(int)} before calling this function. 13 * 14 * @return The previously computed Y velocity. 15 */ 16 public float getYVelocity() { 17 return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID); 18 } 19 ...... 20 private static native float nativeGetXVelocity(long ptr, int id); 21 private static native float nativeGetYVelocity(long ptr, int id);
注释中也明确说明了,获取速度前,必须先计算速度。
5、重置并回收内存
当不再需要使用上述VelocityTracker实例时,需要重置并回收内存,使用方法如下:
mVelocityTracker.recycle();
对应源码如下:
1 /** 2 * Return a VelocityTracker object back to be re-used by others. You must 3 * not touch the object after calling this function. 4 */ 5 public void recycle() { 6 if (mStrategy == null) { 7 clear(); 8 sPool.release(this); 9 } 10 } 11 ...... 12 /** 13 * Reset the velocity tracker back to its initial state. 14 */ 15 public void clear() { 16 nativeClear(mPtr); 17 } 18 ...... 19 private static native void nativeClear(long ptr);
6、使用示例
这里举一个实例来演示上述方法的使用。
1 public class VelocityView extends View { 2 private static final String TAG = "songzheweiwang"; 3 private VelocityTracker mVelocityTracker; 4 5 public VelocityView(Context context, @Nullable AttributeSet attrs) { 6 super(context, attrs); 7 mVelocityTracker = VelocityTracker.obtain(); 8 } 9 10 @Override 11 public boolean onTouchEvent(MotionEvent event) { 12 mVelocityTracker.addMovement(event); 13 switch (event.getActionMasked()) { 14 case MotionEvent.ACTION_UP: 15 mVelocityTracker.computeCurrentVelocity(1000); 16 float xVelocity = mVelocityTracker.getXVelocity(); 17 float yVelocity = mVelocityTracker.getYVelocity(); 18 Log.i(TAG, "xVelocity=" + xVelocity + ";yVelocity=" + yVelocity); 19 break; 20 } 21 return true; 22 } 23 24 @Override 25 protected void onDetachedFromWindow() { 26 Log.i(TAG, "onDetachedFromWindow"); 27 mVelocityTracker.recycle(); 28 super.onDetachedFromWindow(); 29 } 30 }
这里只是简单演示这些方法的使用,仅做参考之用,读者可以根据实际情况来使用它们。
在界面上滑动,使用完后退出该界面,打印log为:
07-16 10:15:18.951 10338-10338/com.example.demos I/songzheweiwang: xVelocity=643.2775;yVelocity=543.7565
07-16 10:15:26.406 10338-10338/com.example.demos I/songzheweiwang: onDetachedFromWindow