| package com.android.launcher3.anim; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.ValueAnimator; |
| import android.graphics.Outline; |
| import android.graphics.Rect; |
| import android.view.View; |
| import android.view.ViewOutlineProvider; |
| |
| /** |
| * A {@link ViewOutlineProvider} that has helper functions to create reveal animations. |
| * This class should be extended so that subclasses can define the reveal shape as the |
| * animation progresses from 0 to 1. |
| */ |
| public abstract class RevealOutlineAnimation extends ViewOutlineProvider { |
| protected Rect mOutline; |
| protected float mOutlineRadius; |
| |
| public RevealOutlineAnimation() { |
| mOutline = new Rect(); |
| } |
| |
| /** Returns whether elevation should be removed for the duration of the reveal animation. */ |
| abstract boolean shouldRemoveElevationDuringAnimation(); |
| /** Sets the progress, from 0 to 1, of the reveal animation. */ |
| abstract void setProgress(float progress); |
| |
| /** |
| * @see #createRevealAnimator(View, boolean, float) where startProgress is set to 0. |
| */ |
| public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) { |
| return createRevealAnimator(revealView, isReversed, 0f /* startProgress */); |
| } |
| |
| /** |
| * Animates the given View's ViewOutline according to {@link #setProgress(float)}. |
| * @param revealView The View whose outline we are animating. |
| * @param isReversed Whether we are hiding rather than revealing the View. |
| * @param startProgress The progress at which to start the newly created animation. Useful if |
| * the previous reveal animation was cancelled and we want to create a new animation where it |
| * left off. Note that if isReversed=true, we start at 1 - startProgress (and go to 0). |
| * @return The Animator, which the caller must start. |
| */ |
| public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed, |
| float startProgress) { |
| ValueAnimator va = isReversed |
| ? ValueAnimator.ofFloat(1f - startProgress, 0f) |
| : ValueAnimator.ofFloat(startProgress, 1f); |
| final float elevation = revealView.getElevation(); |
| |
| va.addListener(new AnimatorListenerAdapter() { |
| private boolean mIsClippedToOutline; |
| private ViewOutlineProvider mOldOutlineProvider; |
| |
| public void onAnimationStart(Animator animation) { |
| mIsClippedToOutline = revealView.getClipToOutline(); |
| mOldOutlineProvider = revealView.getOutlineProvider(); |
| |
| revealView.setOutlineProvider(RevealOutlineAnimation.this); |
| revealView.setClipToOutline(true); |
| if (shouldRemoveElevationDuringAnimation()) { |
| revealView.setTranslationZ(-elevation); |
| } |
| } |
| |
| public void onAnimationEnd(Animator animation) { |
| revealView.setOutlineProvider(mOldOutlineProvider); |
| revealView.setClipToOutline(mIsClippedToOutline); |
| if (shouldRemoveElevationDuringAnimation()) { |
| revealView.setTranslationZ(0); |
| } |
| } |
| |
| }); |
| |
| va.addUpdateListener(v -> { |
| float progress = (Float) v.getAnimatedValue(); |
| setProgress(progress); |
| revealView.invalidateOutline(); |
| }); |
| return va; |
| } |
| |
| @Override |
| public void getOutline(View v, Outline outline) { |
| outline.setRoundRect(mOutline, mOutlineRadius); |
| } |
| |
| public float getRadius() { |
| return mOutlineRadius; |
| } |
| |
| public void getOutline(Rect out) { |
| out.set(mOutline); |
| } |
| } |