1. 程式人生 > >【朝花夕拾】Android自定義View篇之(十)TouchSlop及VelocityTracker

【朝花夕拾】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

&n