summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2015-03-02 16:06:10 -0800
committerAlan Viverette <alanv@google.com>2015-03-09 18:54:25 -0700
commit6a67db41388165aca63d0d5de2830cc096ed930b (patch)
tree8ab7d0b13f730cbf792f5796547a1083a83869b5 /graphics
parent53776a2b3c18f3eb2217e5e3af4dda187d0fee62 (diff)
downloadframeworks_base-6a67db41388165aca63d0d5de2830cc096ed930b.zip
frameworks_base-6a67db41388165aca63d0d5de2830cc096ed930b.tar.gz
frameworks_base-6a67db41388165aca63d0d5de2830cc096ed930b.tar.bz2
Implement bounded ripple animation
Bug: 19431322 Change-Id: I5dc1a28d8675cc6fb036b815d6227113c3f1aa4b
Diffstat (limited to 'graphics')
-rw-r--r--graphics/java/android/graphics/drawable/RippleComponent.java14
-rw-r--r--graphics/java/android/graphics/drawable/RippleDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/RippleForeground.java185
3 files changed, 144 insertions, 59 deletions
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index fd3e06c..79407f7 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -69,7 +69,6 @@ abstract class RippleComponent {
mDensity = density;
- onSetup();
onTargetRadiusChanged(mTargetRadius);
}
@@ -82,7 +81,10 @@ abstract class RippleComponent {
cancel();
mSoftwareAnimator = createSoftwareEnter(fast);
- mSoftwareAnimator.start();
+
+ if (mSoftwareAnimator != null) {
+ mSoftwareAnimator.start();
+ }
}
/**
@@ -250,14 +252,6 @@ abstract class RippleComponent {
// Stub.
}
- /**
- * Called during ripple setup, which occurs before the first enter
- * animation.
- */
- protected void onSetup() {
- // Stub.
- }
-
protected abstract Animator createSoftwareEnter(boolean fast);
protected abstract Animator createSoftwareExit();
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index bc8c7d2..66160c0 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -564,7 +564,9 @@ public class RippleDrawable extends LayerDrawable {
x = mHotspotBounds.exactCenterX();
y = mHotspotBounds.exactCenterY();
}
- mRipple = new RippleForeground(this, mHotspotBounds, x, y);
+
+ final boolean isBounded = !isProjected();
+ mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
}
mRipple.setup(mState.mMaxRadius, mDensity);
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 2023f04..334122d 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -44,9 +44,16 @@ class RippleForeground extends RippleComponent {
private static final float WAVE_TOUCH_UP_ACCELERATION = 3400;
private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
+ // Bounded ripple animation properties.
+ private static final int BOUNDED_ORIGIN_EXIT_DURATION = 300;
+ private static final int BOUNDED_RADIUS_EXIT_DURATION = 800;
+ private static final int BOUNDED_OPACITY_EXIT_DURATION = 400;
+ private static final float MAX_BOUNDED_RADIUS = 350;
+
private static final int RIPPLE_ENTER_DELAY = 80;
private static final int OPACITY_ENTER_DURATION_FAST = 120;
+ // Parent-relative values for starting position.
private float mStartingX;
private float mStartingY;
private float mClampedStartingX;
@@ -58,30 +65,41 @@ class RippleForeground extends RippleComponent {
private CanvasProperty<Float> mPropX;
private CanvasProperty<Float> mPropY;
+ // Target values for tween animations.
+ private float mTargetX = 0;
+ private float mTargetY = 0;
+
+ /** Ripple target radius used when bounded. Not used for clamping. */
+ private float mBoundedRadius = 0;
+
// Software rendering properties.
private float mOpacity = 1;
- private float mOuterX;
- private float mOuterY;
// Values used to tween between the start and end positions.
private float mTweenRadius = 0;
private float mTweenX = 0;
private float mTweenY = 0;
+ /** Whether this ripple is bounded. */
+ private boolean mIsBounded;
+
/** Whether this ripple has finished its exit animation. */
private boolean mHasFinishedExit;
- public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
+ public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
+ boolean isBounded) {
super(owner, bounds);
+ mIsBounded = isBounded;
mStartingX = startingX;
mStartingY = startingY;
- }
- @Override
- public void onSetup() {
- mOuterX = 0;
- mOuterY = 0;
+ if (isBounded) {
+ mBoundedRadius = MAX_BOUNDED_RADIUS * 0.9f
+ + (float) (MAX_BOUNDED_RADIUS * Math.random() * 0.1);
+ } else {
+ mBoundedRadius = 0;
+ }
}
@Override
@@ -95,12 +113,10 @@ class RippleForeground extends RippleComponent {
final int origAlpha = p.getAlpha();
final int alpha = (int) (origAlpha * mOpacity + 0.5f);
- final float radius = MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+ final float radius = getCurrentRadius();
if (alpha > 0 && radius > 0) {
- final float x = MathUtils.lerp(
- mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
- final float y = MathUtils.lerp(
- mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+ final float x = getCurrentX();
+ final float y = getCurrentY();
p.setAlpha(alpha);
c.drawCircle(x, y, radius, p);
p.setAlpha(origAlpha);
@@ -120,8 +136,8 @@ class RippleForeground extends RippleComponent {
* Returns the maximum bounds of the ripple relative to the ripple center.
*/
public void getBounds(Rect bounds) {
- final int outerX = (int) mOuterX;
- final int outerY = (int) mOuterY;
+ final int outerX = (int) mTargetX;
+ final int outerY = (int) mTargetY;
final int r = (int) mTargetRadius + 1;
bounds.set(outerX - r, outerY - r, outerX + r, outerY + r);
}
@@ -146,14 +162,25 @@ class RippleForeground extends RippleComponent {
@Override
protected Animator createSoftwareEnter(boolean fast) {
+ // Bounded ripples don't have enter animations.
+ if (mIsBounded) {
+ return null;
+ }
+
final int duration = (int)
(1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensity) + 0.5);
- final ObjectAnimator tweenAll = ObjectAnimator.ofFloat(this, TWEEN_ALL, 1);
- tweenAll.setAutoCancel(true);
- tweenAll.setDuration(duration);
- tweenAll.setInterpolator(LINEAR_INTERPOLATOR);
- tweenAll.setStartDelay(RIPPLE_ENTER_DELAY);
+ final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
+ tweenRadius.setAutoCancel(true);
+ tweenRadius.setDuration(duration);
+ tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
+ tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
+
+ final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
+ tweenOrigin.setAutoCancel(true);
+ tweenOrigin.setDuration(duration);
+ tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
+ tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
opacity.setAutoCancel(true);
@@ -161,31 +188,68 @@ class RippleForeground extends RippleComponent {
opacity.setInterpolator(LINEAR_INTERPOLATOR);
final AnimatorSet set = new AnimatorSet();
- set.play(tweenAll).with(opacity);
+ set.play(tweenOrigin).with(tweenRadius).with(opacity);
return set;
}
+ private float getCurrentX() {
+ return MathUtils.lerp(mClampedStartingX - mBounds.exactCenterX(), mTargetX, mTweenX);
+ }
+
+ private float getCurrentY() {
+ return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
+ }
+
private int getRadiusExitDuration() {
- final float radius = MathUtils.lerp(0, mTargetRadius, mTweenRadius);
- final float remaining = mTargetRadius - radius;
- return (int) (1000 * Math.sqrt(remaining / (WAVE_TOUCH_UP_ACCELERATION
+ final float remainingRadius = mTargetRadius - getCurrentRadius();
+ return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
+ WAVE_TOUCH_DOWN_ACCELERATION) * mDensity) + 0.5);
}
+ private float getCurrentRadius() {
+ return MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+ }
+
private int getOpacityExitDuration() {
return (int) (1000 * mOpacity / WAVE_OPACITY_DECAY_VELOCITY + 0.5f);
}
+ /**
+ * Compute target values that are dependent on bounding.
+ */
+ private void computeBoundedTargetValues() {
+ mTargetX = (mClampedStartingX - mBounds.exactCenterX()) * .7f;
+ mTargetY = (mClampedStartingY - mBounds.exactCenterY()) * .7f;
+ mTargetRadius = mBoundedRadius;
+ }
+
@Override
protected Animator createSoftwareExit() {
- final int radiusDuration = getRadiusExitDuration();
- final int opacityDuration = getOpacityExitDuration();
+ final int radiusDuration;
+ final int originDuration;
+ final int opacityDuration;
+ if (mIsBounded) {
+ computeBoundedTargetValues();
+
+ radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
+ originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
+ opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
+ } else {
+ radiusDuration = getRadiusExitDuration();
+ originDuration = radiusDuration;
+ opacityDuration = getOpacityExitDuration();
+ }
+
+ final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
+ tweenRadius.setAutoCancel(true);
+ tweenRadius.setDuration(radiusDuration);
+ tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
- final ObjectAnimator tweenAll = ObjectAnimator.ofFloat(this, TWEEN_ALL, 1);
- tweenAll.setAutoCancel(true);
- tweenAll.setDuration(radiusDuration);
- tweenAll.setInterpolator(DECELERATE_INTERPOLATOR);
+ final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
+ tweenOrigin.setAutoCancel(true);
+ tweenOrigin.setDuration(originDuration);
+ tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 0);
opacity.setAutoCancel(true);
@@ -193,7 +257,7 @@ class RippleForeground extends RippleComponent {
opacity.setInterpolator(LINEAR_INTERPOLATOR);
final AnimatorSet set = new AnimatorSet();
- set.play(tweenAll).with(opacity);
+ set.play(tweenOrigin).with(tweenRadius).with(opacity);
set.addListener(mAnimationListener);
return set;
@@ -201,15 +265,25 @@ class RippleForeground extends RippleComponent {
@Override
protected RenderNodeAnimatorSet createHardwareExit(Paint p) {
- final int radiusDuration = getRadiusExitDuration();
- final int opacityDuration = getOpacityExitDuration();
+ final int radiusDuration;
+ final int originDuration;
+ final int opacityDuration;
+ if (mIsBounded) {
+ computeBoundedTargetValues();
+
+ radiusDuration = BOUNDED_RADIUS_EXIT_DURATION;
+ originDuration = BOUNDED_ORIGIN_EXIT_DURATION;
+ opacityDuration = BOUNDED_OPACITY_EXIT_DURATION;
+ } else {
+ radiusDuration = getRadiusExitDuration();
+ originDuration = radiusDuration;
+ opacityDuration = getOpacityExitDuration();
+ }
- final float startX = MathUtils.lerp(
- mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
- final float startY = MathUtils.lerp(
- mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+ final float startX = getCurrentX();
+ final float startY = getCurrentY();
+ final float startRadius = getCurrentRadius();
- final float startRadius = MathUtils.lerp(0, mTargetRadius, mTweenRadius);
p.setAlpha((int) (p.getAlpha() * mOpacity + 0.5f));
mPropPaint = CanvasProperty.createPaint(p);
@@ -221,12 +295,12 @@ class RippleForeground extends RippleComponent {
radius.setDuration(radiusDuration);
radius.setInterpolator(DECELERATE_INTERPOLATOR);
- final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mOuterX);
- x.setDuration(radiusDuration);
+ final RenderNodeAnimator x = new RenderNodeAnimator(mPropX, mTargetX);
+ x.setDuration(originDuration);
x.setInterpolator(DECELERATE_INTERPOLATOR);
- final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mOuterY);
- y.setDuration(radiusDuration);
+ final RenderNodeAnimator y = new RenderNodeAnimator(mPropY, mTargetY);
+ y.setDuration(originDuration);
y.setInterpolator(DECELERATE_INTERPOLATOR);
final RenderNodeAnimator opacity = new RenderNodeAnimator(mPropPaint,
@@ -307,16 +381,13 @@ class RippleForeground extends RippleComponent {
}
/**
- * Property for animating radius, center X, and center Y between their
- * initial and target values.
+ * Property for animating radius between its initial and target values.
*/
- private static final FloatProperty<RippleForeground> TWEEN_ALL =
- new FloatProperty<RippleForeground>("tweenAll") {
+ private static final FloatProperty<RippleForeground> TWEEN_RADIUS =
+ new FloatProperty<RippleForeground>("tweenRadius") {
@Override
public void setValue(RippleForeground object, float value) {
object.mTweenRadius = value;
- object.mTweenX = value;
- object.mTweenY = value;
object.invalidateSelf();
}
@@ -327,6 +398,24 @@ class RippleForeground extends RippleComponent {
};
/**
+ * Property for animating origin between its initial and target values.
+ */
+ private static final FloatProperty<RippleForeground> TWEEN_ORIGIN =
+ new FloatProperty<RippleForeground>("tweenOrigin") {
+ @Override
+ public void setValue(RippleForeground object, float value) {
+ object.mTweenX = value;
+ object.mTweenY = value;
+ object.invalidateSelf();
+ }
+
+ @Override
+ public Float get(RippleForeground object) {
+ return object.mTweenX;
+ }
+ };
+
+ /**
* Property for animating opacity between 0 and its target value.
*/
private static final FloatProperty<RippleForeground> OPACITY =