VelocityTracker.java revision b79781af1ebc68dbaa1b44da43fd391de067a201
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.view; 18 19import android.util.Config; 20import android.util.Log; 21import android.util.Poolable; 22import android.util.Pool; 23import android.util.Pools; 24import android.util.PoolableManager; 25 26/** 27 * Helper for tracking the velocity of touch events, for implementing 28 * flinging and other such gestures. Use {@link #obtain} to retrieve a 29 * new instance of the class when you are going to begin tracking, put 30 * the motion events you receive into it with {@link #addMovement(MotionEvent)}, 31 * and when you want to determine the velocity call 32 * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()} 33 * and {@link #getXVelocity()}. 34 */ 35public final class VelocityTracker implements Poolable<VelocityTracker> { 36 static final String TAG = "VelocityTracker"; 37 static final boolean DEBUG = false; 38 static final boolean localLOGV = DEBUG || Config.LOGV; 39 40 static final int NUM_PAST = 10; 41 static final int LONGEST_PAST_TIME = 200; 42 43 static final VelocityTracker[] mPool = new VelocityTracker[1]; 44 private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( 45 Pools.finitePool(new PoolableManager<VelocityTracker>() { 46 public VelocityTracker newInstance() { 47 return new VelocityTracker(); 48 } 49 50 public void onAcquired(VelocityTracker element) { 51 element.clear(); 52 } 53 54 public void onReleased(VelocityTracker element) { 55 } 56 }, 2)); 57 58 final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; 59 final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; 60 final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; 61 62 float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; 63 float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; 64 int mLastTouch; 65 66 private VelocityTracker mNext; 67 68 /** 69 * Retrieve a new VelocityTracker object to watch the velocity of a 70 * motion. Be sure to call {@link #recycle} when done. You should 71 * generally only maintain an active object while tracking a movement, 72 * so that the VelocityTracker can be re-used elsewhere. 73 * 74 * @return Returns a new VelocityTracker. 75 */ 76 static public VelocityTracker obtain() { 77 return sPool.acquire(); 78 } 79 80 /** 81 * Return a VelocityTracker object back to be re-used by others. You must 82 * not touch the object after calling this function. 83 */ 84 public void recycle() { 85 sPool.release(this); 86 } 87 88 /** 89 * @hide 90 */ 91 public void setNextPoolable(VelocityTracker element) { 92 mNext = element; 93 } 94 95 /** 96 * @hide 97 */ 98 public VelocityTracker getNextPoolable() { 99 return mNext; 100 } 101 102 private VelocityTracker() { 103 } 104 105 /** 106 * Reset the velocity tracker back to its initial state. 107 */ 108 public void clear() { 109 final long[][] pastTime = mPastTime; 110 for (int p = 0; p < MotionEvent.BASE_AVAIL_POINTERS; p++) { 111 for (int i = 0; i < NUM_PAST; i++) { 112 pastTime[p][i] = 0; 113 } 114 } 115 } 116 117 /** 118 * Add a user's movement to the tracker. You should call this for the 119 * initial {@link MotionEvent#ACTION_DOWN}, the following 120 * {@link MotionEvent#ACTION_MOVE} events that you receive, and the 121 * final {@link MotionEvent#ACTION_UP}. You can, however, call this 122 * for whichever events you desire. 123 * 124 * @param ev The MotionEvent you received and would like to track. 125 */ 126 public void addMovement(MotionEvent ev) { 127 long time = ev.getEventTime(); 128 final int N = ev.getHistorySize(); 129 final int pointerCount = ev.getPointerCount(); 130 for (int p = 0; p < pointerCount; p++) { 131 for (int i=0; i<N; i++) { 132 addPoint(p, ev.getHistoricalX(p, i), ev.getHistoricalY(p, i), 133 ev.getHistoricalEventTime(i)); 134 } 135 addPoint(p, ev.getX(p), ev.getY(p), time); 136 } 137 } 138 139 private void addPoint(int pos, float x, float y, long time) { 140 final int lastTouch = (mLastTouch + 1) % NUM_PAST; 141 mPastX[pos][lastTouch] = x; 142 mPastY[pos][lastTouch] = y; 143 mPastTime[pos][lastTouch] = time; 144 mLastTouch = lastTouch; 145 } 146 147 /** 148 * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum 149 * velocity of Float.MAX_VALUE. 150 * 151 * @see #computeCurrentVelocity(int, float) 152 */ 153 public void computeCurrentVelocity(int units) { 154 computeCurrentVelocity(units, Float.MAX_VALUE); 155 } 156 157 /** 158 * Compute the current velocity based on the points that have been 159 * collected. Only call this when you actually want to retrieve velocity 160 * information, as it is relatively expensive. You can then retrieve 161 * the velocity with {@link #getXVelocity()} and 162 * {@link #getYVelocity()}. 163 * 164 * @param units The units you would like the velocity in. A value of 1 165 * provides pixels per millisecond, 1000 provides pixels per second, etc. 166 * @param maxVelocity The maximum velocity that can be computed by this method. 167 * This value must be declared in the same unit as the units parameter. This value 168 * must be positive. 169 */ 170 public void computeCurrentVelocity(int units, float maxVelocity) { 171 for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) { 172 final float[] pastX = mPastX[pos]; 173 final float[] pastY = mPastY[pos]; 174 final long[] pastTime = mPastTime[pos]; 175 final int lastTouch = mLastTouch; 176 177 // find oldest acceptable time 178 int oldestTouch = lastTouch; 179 if (pastTime[lastTouch] > 0) { // cleared ? 180 oldestTouch = (lastTouch + 1) % NUM_PAST; 181 final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME; 182 while (pastTime[oldestTouch] < acceptableTime) { 183 oldestTouch = (oldestTouch + 1) % NUM_PAST; 184 } 185 } 186 187 // Kind-of stupid. 188 final float oldestX = pastX[oldestTouch]; 189 final float oldestY = pastY[oldestTouch]; 190 final long oldestTime = pastTime[oldestTouch]; 191 float accumX = 0; 192 float accumY = 0; 193 int N = (lastTouch - oldestTouch + NUM_PAST) % NUM_PAST + 1; 194 // Skip the last received event, since it is probably pretty noisy. 195 if (N > 3) N--; 196 197 for (int i=1; i < N; i++) { 198 final int j = (oldestTouch + i) % NUM_PAST; 199 final int dur = (int)(pastTime[j] - oldestTime); 200 if (dur == 0) continue; 201 float dist = pastX[j] - oldestX; 202 float vel = (dist/dur) * units; // pixels/frame. 203 accumX = (accumX == 0) ? vel : (accumX + vel) * .5f; 204 205 dist = pastY[j] - oldestY; 206 vel = (dist/dur) * units; // pixels/frame. 207 accumY = (accumY == 0) ? vel : (accumY + vel) * .5f; 208 } 209 210 mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity) 211 : Math.min(accumX, maxVelocity); 212 mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity) 213 : Math.min(accumY, maxVelocity); 214 215 if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" 216 + mXVelocity + " N=" + N); 217 } 218 } 219 220 /** 221 * Retrieve the last computed X velocity. You must first call 222 * {@link #computeCurrentVelocity(int)} before calling this function. 223 * 224 * @return The previously computed X velocity. 225 */ 226 public float getXVelocity() { 227 return mXVelocity[0]; 228 } 229 230 /** 231 * Retrieve the last computed Y velocity. You must first call 232 * {@link #computeCurrentVelocity(int)} before calling this function. 233 * 234 * @return The previously computed Y velocity. 235 */ 236 public float getYVelocity() { 237 return mYVelocity[0]; 238 } 239 240 /** 241 * Retrieve the last computed X velocity. You must first call 242 * {@link #computeCurrentVelocity(int)} before calling this function. 243 * 244 * @param pos Which pointer's velocity to return. 245 * @return The previously computed X velocity. 246 * 247 * @hide Pending API approval 248 */ 249 public float getXVelocity(int pos) { 250 return mXVelocity[pos]; 251 } 252 253 /** 254 * Retrieve the last computed Y velocity. You must first call 255 * {@link #computeCurrentVelocity(int)} before calling this function. 256 * 257 * @param pos Which pointer's velocity to return. 258 * @return The previously computed Y velocity. 259 * 260 * @hide Pending API approval 261 */ 262 public float getYVelocity(int pos) { 263 return mYVelocity[pos]; 264 } 265} 266