summaryrefslogtreecommitdiffstats
path: root/core/java/android/view/VelocityTracker.java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /core/java/android/view/VelocityTracker.java
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'core/java/android/view/VelocityTracker.java')
-rw-r--r--core/java/android/view/VelocityTracker.java215
1 files changed, 215 insertions, 0 deletions
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
new file mode 100644
index 0000000..c80167e
--- /dev/null
+++ b/core/java/android/view/VelocityTracker.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * Helper for tracking the velocity of touch events, for implementing
+ * flinging and other such gestures. Use {@link #obtain} to retrieve a
+ * new instance of the class when you are going to begin tracking, put
+ * the motion events you receive into it with {@link #addMovement(MotionEvent)},
+ * and when you want to determine the velocity call
+ * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}
+ * and {@link #getXVelocity()}.
+ */
+public final class VelocityTracker {
+ static final String TAG = "VelocityTracker";
+ static final boolean DEBUG = false;
+ static final boolean localLOGV = DEBUG || Config.LOGV;
+
+ static final int NUM_PAST = 10;
+ static final int LONGEST_PAST_TIME = 200;
+
+ static final VelocityTracker[] mPool = new VelocityTracker[1];
+
+ final float mPastX[] = new float[NUM_PAST];
+ final float mPastY[] = new float[NUM_PAST];
+ final long mPastTime[] = new long[NUM_PAST];
+
+ float mYVelocity;
+ float mXVelocity;
+
+ /**
+ * Retrieve a new VelocityTracker object to watch the velocity of a
+ * motion. Be sure to call {@link #recycle} when done. You should
+ * generally only maintain an active object while tracking a movement,
+ * so that the VelocityTracker can be re-used elsewhere.
+ *
+ * @return Returns a new VelocityTracker.
+ */
+ static public VelocityTracker obtain() {
+ synchronized (mPool) {
+ VelocityTracker vt = mPool[0];
+ if (vt != null) {
+ vt.clear();
+ return vt;
+ }
+ return new VelocityTracker();
+ }
+ }
+
+ /**
+ * Return a VelocityTracker object back to be re-used by others. You must
+ * not touch the object after calling this function.
+ */
+ public void recycle() {
+ synchronized (mPool) {
+ mPool[0] = this;
+ }
+ }
+
+ private VelocityTracker() {
+ }
+
+ /**
+ * Reset the velocity tracker back to its initial state.
+ */
+ public void clear() {
+ mPastTime[0] = 0;
+ }
+
+ /**
+ * Add a user's movement to the tracker. You should call this for the
+ * initial {@link MotionEvent#ACTION_DOWN}, the following
+ * {@link MotionEvent#ACTION_MOVE} events that you receive, and the
+ * final {@link MotionEvent#ACTION_UP}. You can, however, call this
+ * for whichever events you desire.
+ *
+ * @param ev The MotionEvent you received and would like to track.
+ */
+ public void addMovement(MotionEvent ev) {
+ long time = ev.getEventTime();
+ final int N = ev.getHistorySize();
+ for (int i=0; i<N; i++) {
+ addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
+ ev.getHistoricalEventTime(i));
+ }
+ addPoint(ev.getX(), ev.getY(), time);
+ }
+
+ private void addPoint(float x, float y, long time) {
+ int drop = -1;
+ int i;
+ if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
+ final long[] pastTime = mPastTime;
+ for (i=0; i<NUM_PAST; i++) {
+ if (pastTime[i] == 0) {
+ break;
+ } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
+ if (localLOGV) Log.v(TAG, "Dropping past too old at "
+ + i + " time=" + pastTime[i]);
+ drop = i;
+ }
+ }
+ if (localLOGV) Log.v(TAG, "Add index: " + i);
+ if (i == NUM_PAST && drop < 0) {
+ drop = 0;
+ }
+ if (drop == i) drop--;
+ final float[] pastX = mPastX;
+ final float[] pastY = mPastY;
+ if (drop >= 0) {
+ if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
+ final int start = drop+1;
+ final int count = NUM_PAST-drop-1;
+ System.arraycopy(pastX, start, pastX, 0, count);
+ System.arraycopy(pastY, start, pastY, 0, count);
+ System.arraycopy(pastTime, start, pastTime, 0, count);
+ i -= (drop+1);
+ }
+ pastX[i] = x;
+ pastY[i] = y;
+ pastTime[i] = time;
+ i++;
+ if (i < NUM_PAST) {
+ pastTime[i] = 0;
+ }
+ }
+
+ /**
+ * Compute the current velocity based on the points that have been
+ * collected. Only call this when you actually want to retrieve velocity
+ * information, as it is relatively expensive. You can then retrieve
+ * the velocity with {@link #getXVelocity()} and
+ * {@link #getYVelocity()}.
+ *
+ * @param units The units you would like the velocity in. A value of 1
+ * provides pixels per millisecond, 1000 provides pixels per second, etc.
+ */
+ public void computeCurrentVelocity(int units) {
+ final float[] pastX = mPastX;
+ final float[] pastY = mPastY;
+ final long[] pastTime = mPastTime;
+
+ // Kind-of stupid.
+ final float oldestX = pastX[0];
+ final float oldestY = pastY[0];
+ final long oldestTime = pastTime[0];
+ float accumX = 0;
+ float accumY = 0;
+ int N=0;
+ while (N < NUM_PAST) {
+ if (pastTime[N] == 0) {
+ break;
+ }
+ N++;
+ }
+ // Skip the last received event, since it is probably pretty noisy.
+ if (N > 3) N--;
+
+ for (int i=1; i < N; i++) {
+ final int dur = (int)(pastTime[i] - oldestTime);
+ if (dur == 0) continue;
+ float dist = pastX[i] - oldestX;
+ float vel = (dist/dur) * units; // pixels/frame.
+ if (accumX == 0) accumX = vel;
+ else accumX = (accumX + vel) * .5f;
+
+ dist = pastY[i] - oldestY;
+ vel = (dist/dur) * units; // pixels/frame.
+ if (accumY == 0) accumY = vel;
+ else accumY = (accumY + vel) * .5f;
+ }
+ mXVelocity = accumX;
+ mYVelocity = accumY;
+
+ if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
+ + mXVelocity + " N=" + N);
+ }
+
+ /**
+ * Retrieve the last computed X velocity. You must first call
+ * {@link #computeCurrentVelocity(int)} before calling this function.
+ *
+ * @return The previously computed X velocity.
+ */
+ public float getXVelocity() {
+ return mXVelocity;
+ }
+
+ /**
+ * Retrieve the last computed Y velocity. You must first call
+ * {@link #computeCurrentVelocity(int)} before calling this function.
+ *
+ * @return The previously computed Y velocity.
+ */
+ public float getYVelocity() {
+ return mYVelocity;
+ }
+}