diff options
author | Alan Viverette <alanv@google.com> | 2015-03-02 16:06:10 -0800 |
---|---|---|
committer | Alan Viverette <alanv@google.com> | 2015-03-09 18:54:25 -0700 |
commit | 6a67db41388165aca63d0d5de2830cc096ed930b (patch) | |
tree | 8ab7d0b13f730cbf792f5796547a1083a83869b5 /graphics | |
parent | 53776a2b3c18f3eb2217e5e3af4dda187d0fee62 (diff) | |
download | frameworks_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')
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 = |