diff --git a/app/src/main/java/io/codetail/circualrevealsample/MainActivity.java b/app/src/main/java/io/codetail/circualrevealsample/MainActivity.java index 0801b6d..4cc697a 100644 --- a/app/src/main/java/io/codetail/circualrevealsample/MainActivity.java +++ b/app/src/main/java/io/codetail/circualrevealsample/MainActivity.java @@ -22,6 +22,7 @@ import butterknife.ButterKnife; import butterknife.InjectView; +import io.codetail.animation.Animator; import io.codetail.animation.ViewAnimationUtils; import io.codetail.circualrevealsample.widget.FloatingActionButton; import io.codetail.circualrevealsample.widget.ViewUtils; @@ -59,7 +60,6 @@ protected void onCreate(Bundle savedInstanceState) { mCardsGroup.setClipToPadding(false); mCardsGroup.setAdapter(mCardsAdapter); mCardsGroup.setLayoutManager(mLayoutManager); - mCardsGroup.setOnScrollListener(new HideExtraOnScroll(mToolbar)); mToolbar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override @@ -88,17 +88,33 @@ public void onClick(View v) { // get the final radius for the clipping circle int finalRadius = Math.max(myView.getWidth(), myView.getHeight()) + 100; - ObjectAnimator animator = (ObjectAnimator) + Animator animator = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius); animator.setInterpolator(new AccelerateInterpolator()); animator.setDuration(500); - animator.setAutoCancel(true); + + if(Animator.LOLLIPOP){ + android.animation.ObjectAnimator a = (android.animation.ObjectAnimator) + animator.getNativeAnimator(); + a.setAutoCancel(true); + }else{ + ObjectAnimator a = (ObjectAnimator) + animator.getSupportAnimator(); + a.setAutoCancel(true); + } + animator.start(); } }); } + @Override + protected void onStart() { + super.onStart(); + mCardsGroup.setOnScrollListener(new HideExtraOnScroll(mToolbar)); + } + @Override protected void onStop() { super.onStop(); diff --git a/circualreveal/src/main/java/io/codetail/animation/Animator.java b/circualreveal/src/main/java/io/codetail/animation/Animator.java new file mode 100644 index 0000000..3615469 --- /dev/null +++ b/circualreveal/src/main/java/io/codetail/animation/Animator.java @@ -0,0 +1,230 @@ +package io.codetail.animation; + + +import android.annotation.TargetApi; +import android.os.Build; +import android.view.animation.Interpolator; + +import java.util.ArrayList; + +public class Animator { + + public final static boolean LOLLIPOP = Build.VERSION.SDK_INT >= 21; + + android.animation.Animator mNativeAnimator; + com.nineoldandroids.animation.Animator mSupportAnimator; + + /** + * @hide + */ + public Animator(com.nineoldandroids.animation.Animator animator) { + mSupportAnimator = animator; + } + + /** + * @hide + */ + public Animator(android.animation.Animator animator){ + mNativeAnimator = animator; + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public Animator setInterpolator(Interpolator interpolator){ + if(LOLLIPOP){ + mNativeAnimator.setInterpolator(interpolator); + }else{ + mSupportAnimator.setInterpolator(interpolator); + } + return this; + } + + /** + * Sets the duration of the animation. + * + * @param duration The length of the animation, in milliseconds. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public Animator setDuration(int duration){ + if(LOLLIPOP){ + mNativeAnimator.setDuration(duration); + }else{ + mSupportAnimator.setDuration(duration); + } + return this; + } + + public Animator addListener(com.nineoldandroids.animation.Animator.AnimatorListener listener){ + mSupportAnimator.addListener(listener); + return this; + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public Animator addListener(android.animation.Animator.AnimatorListener listener){ + mNativeAnimator.addListener(listener); + return this; + } + + public Animator addPauseListener(com.nineoldandroids.animation.Animator.AnimatorPauseListener pauseListener){ + mSupportAnimator.addPauseListener(pauseListener); + return this; + } + + @TargetApi(Build.VERSION_CODES.KITKAT) + public Animator addPauseListener(android.animation.Animator.AnimatorPauseListener pauseListener){ + mNativeAnimator.addPauseListener(pauseListener); + return this; + } + + + /** + * Gets the duration of the animation. + * + * @return The length of the animation, in milliseconds. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public long getDuration(){ + if(LOLLIPOP){ + return mNativeAnimator.getDuration(); + }else{ + return mSupportAnimator.getDuration(); + } + } + + /** + * Returns whether this Animator is currently running (having been started and gone past any + * initial startDelay period and not yet ended). + * + * @return Whether the Animator is running. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public boolean isRunning(){ + if(LOLLIPOP){ + return mNativeAnimator.isRunning(); + }else{ + return mSupportAnimator.isRunning(); + } + } + + /** + * Returns whether this Animator has been started and not yet ended. This state is a superset + * of the state of {@link #isRunning()}, because an Animator with a nonzero + * will return true for {@link #isStarted()} during the + * delay phase, whereas {@link #isRunning()} will return true only after the delay phase + * is complete. + * + * @return Whether the Animator has been started and not yet ended. + */ + public boolean isStarted() { + // Default method returns value for isRunning(). Subclasses should override to return a + // real value. + return isRunning(); + } + + /** + * Removes a listener from the set listening to this animation. + * + * @param listener the listener to be removed from the current set of listeners for this + * animation. + */ + public void removeListener(com.nineoldandroids.animation.Animator.AnimatorListener listener) { + mSupportAnimator.removeListener(listener); + } + + /** + * Removes a listener from the set listening to this animation. + * + * @param listener the listener to be removed from the current set of listeners for this + * animation. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void removeListener(android.animation.Animator.AnimatorListener listener) { + mNativeAnimator.removeListener(listener); + } + + /** + * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently + * listening for events on this Animator object. + * + * @return ArrayList The set of listeners. + */ + public ArrayList getSupportListeners() { + return mSupportAnimator.getListeners(); + } + + /** + * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently + * listening for events on this Animator object. + * + * @return ArrayList The set of listeners. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public ArrayList getNativeListeners() { + return mNativeAnimator.getListeners(); + } + + /** + * Removes a pause listener from the set listening to this animation. + * + * @param listener the listener to be removed from the current set of pause + * listeners for this animation. + */ + public void removePauseListener(com.nineoldandroids.animation.Animator.AnimatorPauseListener listener) { + mSupportAnimator.removePauseListener(listener); + } + + /** + * Removes a pause listener from the set listening to this animation. + * + * @param listener the listener to be removed from the current set of pause + * listeners for this animation. + */ + @TargetApi(Build.VERSION_CODES.KITKAT) + public void removePauseListener(android.animation.Animator.AnimatorPauseListener listener) { + mNativeAnimator.removePauseListener(listener); + } + + /** + * Removes all {@link #addListener(com.nineoldandroids.animation.Animator.AnimatorListener)} listeners} + * and {@link #addPauseListener(com.nineoldandroids.animation.Animator.AnimatorPauseListener)} + * pauseListeners} from this object. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void removeAllListeners() { + if(LOLLIPOP){ + mNativeAnimator.removeAllListeners(); + }else{ + mSupportAnimator.removeAllListeners(); + } + } + + /** + * Starts this animation. If the animation has a nonzero startDelay, the animation will start + * running after that delay elapses. A non-delayed animation will have its initial + * value(s) set immediately, followed by calls to AnimationUpdate for any listeners of this animator. + * + *

The animation started by calling this method will be run on the thread that called + * this method. This thread should have a Looper on it (a runtime exception will be thrown if + * this is not the case). Also, if the animation will animate + * properties of objects in the view hierarchy, then the calling thread should be the UI + * thread for that view hierarchy.

+ * + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void start() { + if(LOLLIPOP){ + mNativeAnimator.start(); + }else{ + mSupportAnimator.start(); + } + } + + + public com.nineoldandroids.animation.Animator getSupportAnimator(){ + return mSupportAnimator; + } + + public android.animation.Animator getNativeAnimator(){ + return mNativeAnimator; + } + +} diff --git a/circualreveal/src/main/java/io/codetail/animation/ViewAnimationUtils.java b/circualreveal/src/main/java/io/codetail/animation/ViewAnimationUtils.java index a0396ce..e75365e 100644 --- a/circualreveal/src/main/java/io/codetail/animation/ViewAnimationUtils.java +++ b/circualreveal/src/main/java/io/codetail/animation/ViewAnimationUtils.java @@ -31,10 +31,15 @@ public class ViewAnimationUtils { * @param startRadius The starting radius of the animating circle. * @param endRadius The ending radius of the animating circle. */ - public static Animator createCircularReveal(View view, + public static io.codetail.animation.Animator createCircularReveal(View view, int centerX, int centerY, float startRadius, float endRadius) { + if(io.codetail.animation.Animator.LOLLIPOP){ + return new io.codetail.animation.Animator(android.view.ViewAnimationUtils + .createCircularReveal(view, centerX, centerY, startRadius, endRadius)); + } + if(!(view.getParent() instanceof RevealAnimator)){ throw new IllegalArgumentException("View must be inside dreamers.widget.RevealLayout."); } @@ -46,10 +51,10 @@ public static Animator createCircularReveal(View view, Rect bounds = new Rect(); view.getHitRect(bounds); - Animator reveal = ObjectAnimator.ofFloat(revealLayout, "revealRadius", startRadius, endRadius); + ObjectAnimator reveal = ObjectAnimator.ofFloat(revealLayout, "revealRadius", startRadius, endRadius); reveal.addListener(new RevealAnimator.RevealFinished(revealLayout, bounds)); - return reveal; + return new io.codetail.animation.Animator(reveal); } /** diff --git a/circualreveal/src/main/java/io/codetail/widget/FrameLayoutCompat.java b/circualreveal/src/main/java/io/codetail/widget/FrameLayoutCompat.java index f7af3a8..1805591 100644 --- a/circualreveal/src/main/java/io/codetail/widget/FrameLayoutCompat.java +++ b/circualreveal/src/main/java/io/codetail/widget/FrameLayoutCompat.java @@ -23,15 +23,16 @@ import java.util.ArrayList; +import io.codetail.animation.Animator; import io.codetail.view.R; // Port of FrameLayout for Android 2.3 Gingerbread public class FrameLayoutCompat extends ViewGroup{ - private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START; - private static final boolean FEATURES_HONEYCOMB = Build.VERSION.SDK_INT > 10; - private static final boolean FEATURES_KITKAT = Build.VERSION.SDK_INT > 16; - private static final boolean FEATURES_LOLLIPOP = Build.VERSION.SDK_INT > 16; + static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START; + static final boolean FEATURES_HONEYCOMB = Build.VERSION.SDK_INT > 10; + static final boolean FEATURES_KITKAT = Build.VERSION.SDK_INT > 16; + static final boolean FEATURES_LOLLIPOP = Animator.LOLLIPOP; boolean mMeasureAllChildren = false; diff --git a/circualreveal/src/main/java/io/codetail/widget/RevealFrameLayout.java b/circualreveal/src/main/java/io/codetail/widget/RevealFrameLayout.java index 2a320d5..dcc62a4 100644 --- a/circualreveal/src/main/java/io/codetail/widget/RevealFrameLayout.java +++ b/circualreveal/src/main/java/io/codetail/widget/RevealFrameLayout.java @@ -32,6 +32,10 @@ public RevealFrameLayout(Context context, AttributeSet attrs) { public RevealFrameLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + if(FEATURES_HONEYCOMB && !FEATURES_KITKAT){ + setLayerType(LAYER_TYPE_SOFTWARE, null); + } + mRevealPath = new Path(); }