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**. 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 0000000..c278b6c Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/ic_done.png differ 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 0000000..6d84e14 Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/ic_done.png differ 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 0000000..3b2b65d Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/ic_done.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/ic_done.png b/sample/src/main/res/drawable-xxhdpi/ic_done.png new file mode 100644 index 0000000..0ebb555 Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/ic_done.png differ 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