summaryrefslogtreecommitdiffstats
path: root/core/java/com/android/internal/widget/RotarySelector.java
diff options
context:
space:
mode:
authorKarl Rosaen <krosaen@android.com>2009-09-22 11:36:23 -0700
committerKarl Rosaen <krosaen@android.com>2009-09-22 11:36:23 -0700
commit896264f603ad90e58557c5d3f34bc6389fff3fc5 (patch)
treebb8069a3f913397f7770c886fffe59a008b80cf2 /core/java/com/android/internal/widget/RotarySelector.java
parente256c79731ecd412fbf0d566ea6c844ea8e972a1 (diff)
downloadframeworks_base-896264f603ad90e58557c5d3f34bc6389fff3fc5.zip
frameworks_base-896264f603ad90e58557c5d3f34bc6389fff3fc5.tar.gz
frameworks_base-896264f603ad90e58557c5d3f34bc6389fff3fc5.tar.bz2
Bring the dimples back, and make the "spin around" animation have a fling feeling.
Also clean up the animation code into start/update animation functions.
Diffstat (limited to 'core/java/com/android/internal/widget/RotarySelector.java')
-rw-r--r--core/java/com/android/internal/widget/RotarySelector.java259
1 files changed, 177 insertions, 82 deletions
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
index f610211..426cef5 100644
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -25,8 +25,9 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
-import android.view.SoundEffectConstants;
-import android.view.animation.AccelerateInterpolator;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.animation.DecelerateInterpolator;
import static android.view.animation.AnimationUtils.currentAnimationTimeMillis;
import com.android.internal.R;
@@ -60,16 +61,18 @@ public class RotarySelector extends View {
private int mLeftHandleX;
private int mRightHandleX;
- // current offset of user's dragging
- private int mTouchDragOffset = 0;
+ // current offset of rotary widget along the x axis
+ private int mRotaryOffsetX = 0;
// state of the animation used to bring the handle back to its start position when
// the user lets go before triggering an action
private boolean mAnimating = false;
- private long mAnimationStartTime; // set to the end point of the animatino
+ private long mAnimationStartTime;
private long mAnimationDuration;
- private int mAnimatingDeltaXStart; // the animation will interpolate from this delta down to zero
- private AccelerateInterpolator mInterpolator;
+ private int mAnimatingDeltaXStart; // the animation will interpolate from this delta to zero
+ private int mAnimatingDeltaXEnd;
+
+ private DecelerateInterpolator mInterpolator;
/**
* If the user is currently dragging something.
@@ -87,8 +90,8 @@ public class RotarySelector extends View {
// Vibration (haptic feedback)
private Vibrator mVibrator;
- private static final long VIBRATE_SHORT = 60; // msec
- private static final long VIBRATE_LONG = 100; // msec
+ private static final long VIBRATE_SHORT = 30; // msec
+ private static final long VIBRATE_LONG = 60; // msec
/**
* The drawable for the arrows need to be scrunched this many dips towards the rotary bg below
@@ -114,11 +117,27 @@ public class RotarySelector extends View {
static final int SNAP_BACK_ANIMATION_DURATION_MILLIS = 300;
static final int SPIN_ANIMATION_DURATION_MILLIS = 800;
- private static final boolean DRAW_CENTER_DIMPLE = false;
+ private static final boolean DRAW_CENTER_DIMPLE = true;
private int mEdgeTriggerThresh;
private int mDimpleWidth;
private int mBackgroundWidth;
private int mBackgroundHeight;
+ private final int mOuterRadius;
+ private final int mInnerRadius;
+ private int mDimpleSpacing;
+
+ private VelocityTracker mVelocityTracker;
+ private int mMinimumVelocity;
+ private int mMaximumVelocity;
+
+ /**
+ * The number of dimples we are flinging when we do the "spin" animation. Used to know when to
+ * wrap the icons back around so they "rotate back" onto the screen.
+ * @see #updateAnimation()
+ */
+ private int mDimplesOfFling = 0;
+
+
public RotarySelector(Context context) {
this(context, null);
@@ -152,7 +171,7 @@ public class RotarySelector extends View {
mArrowLongLeft.setBounds(0, 0, arrowW, arrowH);
mArrowLongRight.setBounds(0, 0, arrowW, arrowH);
- mInterpolator = new AccelerateInterpolator();
+ mInterpolator = new DecelerateInterpolator(1f);
mEdgeTriggerThresh = (int) (mDensity * EDGE_TRIGGER_DIP);
@@ -160,6 +179,23 @@ public class RotarySelector extends View {
mBackgroundWidth = mBackground.getIntrinsicWidth();
mBackgroundHeight = mBackground.getIntrinsicHeight();
+ mOuterRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP);
+ mInnerRadius = (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity);
+
+ final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+ mMinimumVelocity = configuration.getScaledMinimumFlingVelocity() * 2;
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimpleWidth / 2;
+ mRightHandleX =
+ getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimpleWidth / 2;
+
+ mDimpleSpacing = (getWidth() / 2) - mLeftHandleX;
}
/**
@@ -229,43 +265,21 @@ public class RotarySelector extends View {
setMeasuredDimension(width, backgroundH + arrowH - arrowScrunch);
}
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
-
- mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimpleWidth / 2;
- mRightHandleX =
- getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimpleWidth / 2;
- }
-
// private Paint mPaint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (DBG) {
- log(String.format("onDraw: mAnimating=%s, mTouchDragOffset=%d, mGrabbedState=%d",
- mAnimating, mTouchDragOffset, mGrabbedState));
+ log(String.format("onDraw: mAnimating=%s, mRotaryOffsetX=%d, mGrabbedState=%d",
+ mAnimating, mRotaryOffsetX, mGrabbedState));
}
final int height = getHeight();
// update animating state before we draw anything
if (mAnimating) {
- final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime;
- final long millisLeft = mAnimationDuration - millisSoFar;
- if (DBG) log("millisleft for animating: " + millisLeft);
- if (millisLeft <= 0) {
- reset();
- } else {
- // we always use the snap back duration as the denominator for interpolation
- // to get a consistent velocity (bascially this makes us happy for the snap back
- // and the spin around one).
- final long denom = SNAP_BACK_ANIMATION_DURATION_MILLIS; // mAnimationDuration
- float interpolation = mInterpolator.getInterpolation(
- (float) millisLeft / denom);
- mTouchDragOffset = (int) (mAnimatingDeltaXStart * interpolation);
- }
+ updateAnimation();
}
// Background:
@@ -302,16 +316,13 @@ public class RotarySelector extends View {
// float or = OUTER_ROTARY_RADIUS_DIP * mDensity;
// canvas.drawCircle(getWidth() / 2, or + mBackground.getBounds().top, or, mPaint);
- final int outerRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP);
- final int innerRadius =
- (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity);
final int bgTop = mBackground.getBounds().top;
{
- final int xOffset = mLeftHandleX + mTouchDragOffset;
+ final int xOffset = mLeftHandleX + mRotaryOffsetX;
final int drawableY = getYOnArc(
mBackground,
- innerRadius,
- outerRadius,
+ mInnerRadius,
+ mOuterRadius,
xOffset);
drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
@@ -321,22 +332,22 @@ public class RotarySelector extends View {
}
if (DRAW_CENTER_DIMPLE) {
- final int xOffset = getWidth() / 2 + mTouchDragOffset;
+ final int xOffset = getWidth() / 2 + mRotaryOffsetX;
final int drawableY = getYOnArc(
mBackground,
- innerRadius,
- outerRadius,
+ mInnerRadius,
+ mOuterRadius,
xOffset);
drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
}
{
- final int xOffset = mRightHandleX + mTouchDragOffset;
+ final int xOffset = mRightHandleX + mRotaryOffsetX;
final int drawableY = getYOnArc(
mBackground,
- innerRadius,
- outerRadius,
+ mInnerRadius,
+ mOuterRadius,
xOffset);
drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
@@ -345,7 +356,33 @@ public class RotarySelector extends View {
}
}
- if (mAnimating) invalidate();
+ // draw extra left hand dimples
+ int dimpleLeft = mRotaryOffsetX + mLeftHandleX - mDimpleSpacing;
+ final int halfdimple = mDimpleWidth / 2;
+ while (dimpleLeft > -halfdimple) {
+ final int drawableY = getYOnArc(
+ mBackground,
+ mInnerRadius,
+ mOuterRadius,
+ dimpleLeft);
+
+ drawCentered(mDimple, canvas, dimpleLeft, drawableY + bgTop);
+ dimpleLeft -= mDimpleSpacing;
+ }
+
+ // draw extra right hand dimples
+ int dimpleRight = mRotaryOffsetX + mRightHandleX + mDimpleSpacing;
+ final int rightThresh = mRight + halfdimple;
+ while (dimpleRight < rightThresh) {
+ final int drawableY = getYOnArc(
+ mBackground,
+ mInnerRadius,
+ mOuterRadius,
+ dimpleRight);
+
+ drawCentered(mDimple, canvas, dimpleRight, drawableY + bgTop);
+ dimpleRight += mDimpleSpacing;
+ }
}
/**
@@ -395,6 +432,11 @@ public class RotarySelector extends View {
if (mAnimating) {
return true;
}
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(event);
+
final int eventX = (int) event.getX();
final int hitWindow = mDimpleWidth;
@@ -409,12 +451,12 @@ public class RotarySelector extends View {
invalidate();
}
if (eventX < mLeftHandleX + hitWindow) {
- mTouchDragOffset = eventX - mLeftHandleX;
+ mRotaryOffsetX = eventX - mLeftHandleX;
mGrabbedState = LEFT_HANDLE_GRABBED;
invalidate();
vibrate(VIBRATE_SHORT);
} else if (eventX > mRightHandleX - hitWindow) {
- mTouchDragOffset = eventX - mRightHandleX;
+ mRotaryOffsetX = eventX - mRightHandleX;
mGrabbedState = RIGHT_HANDLE_GRABBED;
invalidate();
vibrate(VIBRATE_SHORT);
@@ -424,35 +466,38 @@ public class RotarySelector extends View {
case MotionEvent.ACTION_MOVE:
if (DBG) log("touch-move");
if (mGrabbedState == LEFT_HANDLE_GRABBED) {
- mTouchDragOffset = eventX - mLeftHandleX;
+ mRotaryOffsetX = eventX - mLeftHandleX;
invalidate();
if (eventX >= getRight() - mEdgeTriggerThresh && !mTriggered) {
mTriggered = true;
dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE);
- // set up "spin around animation"
- mAnimating = true;
- mAnimationStartTime = currentAnimationTimeMillis();
- mAnimationDuration = SPIN_ANIMATION_DURATION_MILLIS;
- mAnimatingDeltaXStart = -mBackgroundWidth*3;
- mTouchDragOffset = 0;
- mGrabbedState = NOTHING_GRABBED;
- invalidate();
-
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ final int velocity = Math.max(mMinimumVelocity, (int) velocityTracker.getXVelocity());
+ mDimplesOfFling = Math.max(
+ 8,
+ Math.abs(velocity / mDimpleSpacing));
+ startAnimationWithVelocity(
+ eventX - mLeftHandleX,
+ mDimplesOfFling * mDimpleSpacing,
+ velocity);
}
} else if (mGrabbedState == RIGHT_HANDLE_GRABBED) {
- mTouchDragOffset = eventX - mRightHandleX;
+ mRotaryOffsetX = eventX - mRightHandleX;
invalidate();
if (eventX <= mEdgeTriggerThresh && !mTriggered) {
mTriggered = true;
dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE);
- // set up "spin around animation"
- mAnimating = true;
- mAnimationStartTime = currentAnimationTimeMillis();
- mAnimationDuration = SPIN_ANIMATION_DURATION_MILLIS;
- mAnimatingDeltaXStart = mBackgroundWidth*3;
- mTouchDragOffset = 0;
- mGrabbedState = NOTHING_GRABBED;
- invalidate();
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ final int velocity = Math.min(-mMinimumVelocity, (int) velocityTracker.getXVelocity());
+ mDimplesOfFling = Math.max(
+ 8,
+ Math.abs(velocity / mDimpleSpacing));
+ startAnimationWithVelocity(
+ eventX - mRightHandleX,
+ -(mDimplesOfFling * mDimpleSpacing),
+ velocity);
}
}
break;
@@ -462,35 +507,84 @@ public class RotarySelector extends View {
if (mGrabbedState == LEFT_HANDLE_GRABBED
&& Math.abs(eventX - mLeftHandleX) > 5) {
// set up "snap back" animation
- mAnimating = true;
- mAnimationStartTime = currentAnimationTimeMillis();
- mAnimationDuration = SNAP_BACK_ANIMATION_DURATION_MILLIS;
- mAnimatingDeltaXStart = eventX - mLeftHandleX;
+ startAnimation(eventX - mLeftHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS);
} else if (mGrabbedState == RIGHT_HANDLE_GRABBED
&& Math.abs(eventX - mRightHandleX) > 5) {
// set up "snap back" animation
- mAnimating = true;
- mAnimationStartTime = currentAnimationTimeMillis();
- mAnimationDuration = SNAP_BACK_ANIMATION_DURATION_MILLIS;
- mAnimatingDeltaXStart = eventX - mRightHandleX;
+ startAnimation(eventX - mRightHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS);
}
-
- mTouchDragOffset = 0;
+ mRotaryOffsetX = 0;
mGrabbedState = NOTHING_GRABBED;
invalidate();
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle(); // wishin' we had generational GC
+ mVelocityTracker = null;
+ }
break;
case MotionEvent.ACTION_CANCEL:
if (DBG) log("touch-cancel");
reset();
invalidate();
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
break;
}
return true;
}
+ private void startAnimation(int startX, int endX, int duration) {
+ mAnimating = true;
+ mAnimationStartTime = currentAnimationTimeMillis();
+ mAnimationDuration = duration;
+ mAnimatingDeltaXStart = startX;
+ mAnimatingDeltaXEnd = endX;
+ mGrabbedState = NOTHING_GRABBED;
+ mDimplesOfFling = 0;
+ invalidate();
+ }
+
+ private void startAnimationWithVelocity(int startX, int endX, int pixelsPerSecond) {
+ mAnimating = true;
+ mAnimationStartTime = currentAnimationTimeMillis();
+ mAnimationDuration = 1000 * (endX - startX) / pixelsPerSecond;
+ mAnimatingDeltaXStart = startX;
+ mAnimatingDeltaXEnd = endX;
+ mGrabbedState = NOTHING_GRABBED;
+ invalidate();
+ }
+
+ private void updateAnimation() {
+ final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime;
+ final long millisLeft = mAnimationDuration - millisSoFar;
+ final int totalDeltaX = mAnimatingDeltaXStart - mAnimatingDeltaXEnd;
+ if (DBG) log("millisleft for animating: " + millisLeft);
+ if (millisLeft <= 0) {
+ reset();
+ return;
+ }
+ // from 0 to 1 as animation progresses
+ float interpolation =
+ mInterpolator.getInterpolation((float) millisSoFar / mAnimationDuration);
+ final int dx = (int) (totalDeltaX * (1 - interpolation));
+ mRotaryOffsetX = mAnimatingDeltaXEnd + dx;
+ if (mDimplesOfFling > 0) {
+ if (mRotaryOffsetX < 4 * mDimpleSpacing) {
+ // wrap around on fling left
+ mRotaryOffsetX += (4 + mDimplesOfFling - 4) * mDimpleSpacing;
+ } else if (mRotaryOffsetX > 4 * mDimpleSpacing) {
+ // wrap around on fling right
+ mRotaryOffsetX -= (4 + mDimplesOfFling - 4) * mDimpleSpacing;
+ }
+ }
+ invalidate();
+ }
+
private void reset() {
mAnimating = false;
- mTouchDragOffset = 0;
+ mRotaryOffsetX = 0;
+ mDimplesOfFling = 0;
mGrabbedState = NOTHING_GRABBED;
mTriggered = false;
}
@@ -500,7 +594,8 @@ public class RotarySelector extends View {
*/
private synchronized void vibrate(long duration) {
if (mVibrator == null) {
- mVibrator = (android.os.Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
+ mVibrator = (android.os.Vibrator)
+ getContext().getSystemService(Context.VIBRATOR_SERVICE);
}
mVibrator.vibrate(duration);
}