From d0bf36364da71e80117a8395fa3f7ccfeda2ee49 Mon Sep 17 00:00:00 2001 From: drobertson Date: Wed, 9 Dec 2015 15:42:06 -0800 Subject: [PATCH 1/2] Added success and failure animations --- .../clans/fab/FloatingActionButton.java | 98 +++++++++++++++++- library/src/main/res/values/attrs.xml | 6 ++ .../fab/sample/RecyclerViewActivity.java | 15 +++ sample/src/main/res/drawable-hdpi/ic_done.png | Bin 0 -> 188 bytes sample/src/main/res/drawable-mdpi/ic_done.png | Bin 0 -> 139 bytes .../src/main/res/drawable-xhdpi/ic_done.png | Bin 0 -> 199 bytes .../src/main/res/drawable-xxhdpi/ic_done.png | Bin 0 -> 255 bytes .../main/res/layout/recyclerview_activity.xml | 21 ++++ 8 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 sample/src/main/res/drawable-hdpi/ic_done.png create mode 100644 sample/src/main/res/drawable-mdpi/ic_done.png create mode 100644 sample/src/main/res/drawable-xhdpi/ic_done.png create mode 100644 sample/src/main/res/drawable-xxhdpi/ic_done.png diff --git a/library/src/main/java/com/github/clans/fab/FloatingActionButton.java b/library/src/main/java/com/github/clans/fab/FloatingActionButton.java index ea72dd2..deb0322 100755 --- a/library/src/main/java/com/github/clans/fab/FloatingActionButton.java +++ b/library/src/main/java/com/github/clans/fab/FloatingActionButton.java @@ -1,6 +1,9 @@ package com.github.clans.fab; -import android.animation.LayoutTransition; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; @@ -20,6 +23,7 @@ import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.TransitionDrawable; import android.graphics.drawable.shapes.OvalShape; import android.graphics.drawable.shapes.Shape; import android.os.Build; @@ -32,7 +36,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; -import android.view.ViewParent; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.ImageButton; @@ -54,20 +57,29 @@ public class FloatingActionButton extends ImageButton { private static final long PAUSE_GROWING_TIME = 200; private static final double BAR_SPIN_CYCLE_TIME = 500; private static final int BAR_MAX_LENGTH = 270; + private static final int DEFAULT_TRANSITION_ANIMATION_TIME = 300; + private static final int DEFAULT_TRANSITION_ANIMATION_WAIT_TIME = 2750; private int mColorNormal; private int mColorPressed; private int mColorDisabled; private int mColorRipple; + private int mColorSuccess; + private int mColorFailure; private Drawable mIcon; private int mIconSize = Util.dpToPx(getContext(), 24f); private Animation mShowAnimation; private Animation mHideAnimation; + private AnimatorSet mSuccessFailureStateAnimatorSet; private String mLabelText; private OnClickListener mClickListener; private Drawable mBackgroundDrawable; private boolean mUsingElevation; private boolean mUsingElevationCompat; + private Drawable mSuccessDrawable; + private Drawable mFailureDrawable; + private int mSuccessFailureAnimationTime; + private int mSuccessFailureAnimationWaitTime; // Progress private boolean mProgressBarEnabled; @@ -135,6 +147,12 @@ private void init(Context context, AttributeSet attrs, int defStyleAttr) { mProgressBackgroundColor = attr.getColor(R.styleable.FloatingActionButton_fab_progress_backgroundColor, 0x4D000000); mProgressMax = attr.getInt(R.styleable.FloatingActionButton_fab_progress_max, mProgressMax); mShowProgressBackground = attr.getBoolean(R.styleable.FloatingActionButton_fab_progress_showBackground, true); + mSuccessDrawable = attr.getDrawable(R.styleable.FloatingActionButton_fab_icon_success); + mFailureDrawable = attr.getDrawable(R.styleable.FloatingActionButton_fab_icon_failure); + mColorSuccess = attr.getColor(R.styleable.FloatingActionButton_fab_color_success, 0xFF84CA4B); + mColorFailure = attr.getColor(R.styleable.FloatingActionButton_fab_color_failure, 0xFFF8AE41); + mSuccessFailureAnimationTime = attr.getInt(R.styleable.FloatingActionButton_fab_success_failure_transition_animation_time, DEFAULT_TRANSITION_ANIMATION_TIME); + mSuccessFailureAnimationWaitTime = attr.getInt(R.styleable.FloatingActionButton_fab_success_failure_transition_animation_wait_time, DEFAULT_TRANSITION_ANIMATION_WAIT_TIME); if (attr.hasValue(R.styleable.FloatingActionButton_fab_progress)) { mProgress = attr.getInt(R.styleable.FloatingActionButton_fab_progress, 0); @@ -1295,4 +1313,80 @@ public void showButtonInMenu(boolean animate) { label.show(animate); } } + + public void animateFailure() { + animateFromToFrom(mIcon, mFailureDrawable, mColorNormal, mColorFailure); + } + + public void animateSuccess() { + animateFromToFrom(mIcon, mSuccessDrawable, mColorNormal, mColorSuccess); + } + + public boolean isSuccessOrFailureAnimationRunning() { + if (mSuccessFailureStateAnimatorSet != null && mSuccessFailureStateAnimatorSet.isRunning()) { + return true; + } + return false; + } + + public void animateFromToFrom(Drawable animateFromDrawable, + Drawable animateToDrawable, + Integer colorFrom, + Integer colorTo) { + if (isSuccessOrFailureAnimationRunning()) { + return; + } + Drawable drawableStates[] = new Drawable[2]; + drawableStates[0] = animateFromDrawable; + drawableStates[1] = animateToDrawable; + final TransitionDrawable transitionDrawable = new TransitionDrawable(drawableStates); + transitionDrawable.setCrossFadeEnabled(true); + setImageDrawable(transitionDrawable); + + transitionDrawable.startTransition(mSuccessFailureAnimationTime); + + ValueAnimator changeColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo); + changeColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + setColorNormal((Integer) animator.getAnimatedValue()); + } + }); + changeColorAnimation.setDuration(mSuccessFailureAnimationTime); + + ValueAnimator doNothingAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo); + doNothingAnimation.setDuration(mSuccessFailureAnimationWaitTime); + + ValueAnimator restoreColorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorTo, colorFrom); + restoreColorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + setColorNormal((Integer) animator.getAnimatedValue()); + } + }); + restoreColorAnimation.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + transitionDrawable.reverseTransition(mSuccessFailureAnimationTime); + } + + @Override + public void onAnimationEnd(Animator animation) { + setImageDrawable(transitionDrawable.getDrawable(0)); + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }); + restoreColorAnimation.setDuration(mSuccessFailureAnimationTime); + + mSuccessFailureStateAnimatorSet = new AnimatorSet(); + mSuccessFailureStateAnimatorSet.playSequentially(changeColorAnimation, doNothingAnimation, restoreColorAnimation); + mSuccessFailureStateAnimatorSet.start(); + } } diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 6348758..96390b6 100755 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -25,6 +25,12 @@ + + + + + + diff --git a/sample/src/main/java/com/github/clans/fab/sample/RecyclerViewActivity.java b/sample/src/main/java/com/github/clans/fab/sample/RecyclerViewActivity.java index b11c92d..68c5cf7 100644 --- a/sample/src/main/java/com/github/clans/fab/sample/RecyclerViewActivity.java +++ b/sample/src/main/java/com/github/clans/fab/sample/RecyclerViewActivity.java @@ -38,6 +38,7 @@ protected void onCreate(Bundle savedInstanceState) { } final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + final FloatingActionButton loadingSuccessFab = (FloatingActionButton) findViewById(R.id.fab_loading_success); fab.setMax(mMaxProgress); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); @@ -92,6 +93,20 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { } } }); + + loadingSuccessFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + loadingSuccessFab.setIndeterminate(true); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + loadingSuccessFab.animateSuccess(); + loadingSuccessFab.setIndeterminate(false); + } + }, 1500); + } + }); } private void increaseProgress(final FloatingActionButton fab, int i) { diff --git a/sample/src/main/res/drawable-hdpi/ic_done.png b/sample/src/main/res/drawable-hdpi/ic_done.png new file mode 100644 index 0000000000000000000000000000000000000000..c278b6c2b30ba9bb52391af71120270e908a7502 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K8v!{z=h{y4_R}8rt40u`}^1XVn zvWkOUU-^j{pVq65HqD1Oi)r3+T*LWdLcQO)Q$gY0SWi# mI0*h{o5b;0Guz~Cq_l|d_dwzLnQwv4VeoYIb6Mw<&;$SqxI>Tt literal 0 HcmV?d00001 diff --git a/sample/src/main/res/drawable-mdpi/ic_done.png b/sample/src/main/res/drawable-mdpi/ic_done.png new file mode 100644 index 0000000000000000000000000000000000000000..6d84e1431bafe464f26cb2f63061c4c9b57edb10 GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+iAWs*^5R2aA1PRt12kLb%O=-GeF>qN`e$ mOkm7ZjI#{k);#6T#=sC9S)hEq`Oa^k=?tE(elF{r5}E+GHZxWL literal 0 HcmV?d00001 diff --git a/sample/src/main/res/drawable-xhdpi/ic_done.png b/sample/src/main/res/drawable-xhdpi/ic_done.png new file mode 100644 index 0000000000000000000000000000000000000000..3b2b65d26291575f2741d223cdf80facb436dc20 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DK2I0Nkcif|mv{3vD2TKsT6rJ; zxO0YV_Rhw~KO`R95NK`?{Vrkn{Yy=}0*5fKp)HP1G&I6Iqc zjKz5rgEVtukiIy2Qo_-K;J|?c2M!!K@i?}(lWik$uJ;4aeo|e|;mI*SsV?U@bDX5g zSJ{Te;g>S0!OODIPipkgGo)TTcRZxVWAGnIFqKOr(UM3=(~BhBSvit;g=R?Vjh>M7 zAB+M?!6}eboF615Cqq(m7NiKA4^kA)n-qydL_|cirW>R3em?os7vTT^002ovPDHLk FV1hF8Y9jyu literal 0 HcmV?d00001 diff --git a/sample/src/main/res/layout/recyclerview_activity.xml b/sample/src/main/res/layout/recyclerview_activity.xml index 8407a59..878d84a 100644 --- a/sample/src/main/res/layout/recyclerview_activity.xml +++ b/sample/src/main/res/layout/recyclerview_activity.xml @@ -21,4 +21,25 @@ fab:fab_showAnimation="@anim/show_from_bottom" fab:fab_hideAnimation="@anim/hide_to_bottom"/> + + \ No newline at end of file From baeea2d9d5b92c65f74e605a22f143c75cc244c3 Mon Sep 17 00:00:00 2001 From: Daniel Robertson Date: Wed, 9 Dec 2015 15:42:34 -0800 Subject: [PATCH 2/2] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e18bc0..465e97e 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,13 @@ Here are all the **FloatingActionButton**'s xml attributes with their **default app:fab_progress_indeterminate="false" app:fab_progress_max="100" app:fab_progress="0" - app:fab_progress_showBackground="true"/> + app:fab_progress_showBackground="true" + fab:fab_icon_success="@drawable/your_drawable" + fab:fab_icon_failure="@drawable/your_drawable" + fab:fab_color_success="#84CA4B" + fab:fab_color_failure="#F8AE41" + fab:fab_success_failure_transition_animation_time="300" + fab:fab_success_failure_transition_animation_wait_time="2750"/> ``` All of these **FloatingActionButton**'s attributes has their corresponding getters and setters. So you can set them **programmatically**.