Created
October 16, 2018 19:59
-
-
Save tunjid/b2f559ae50c107f538955f3a4b305f4d to your computer and use it in GitHub Desktop.
A simple utility class that creates an expandable Floating Action Button
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mainstreetcode.teammate.util; | |
import android.animation.AnimatorSet; | |
import android.animation.ObjectAnimator; | |
import android.support.annotation.DrawableRes; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import android.support.annotation.StringRes; | |
import android.support.constraint.ConstraintLayout; | |
import android.support.constraint.ConstraintSet; | |
import android.support.design.button.MaterialButton; | |
import android.transition.AutoTransition; | |
import android.transition.Transition; | |
import android.transition.TransitionManager; | |
import android.view.View; | |
import com.mainstreetcode.teammate.R; | |
import java.util.concurrent.atomic.AtomicBoolean; | |
public class FabIconAnimator { | |
private static final String ROTATION_Y_PROPERTY = "rotationY"; | |
private static final float TWITCH_END = 20F; | |
private static final float TWITCH_START = 0F; | |
private static final int DURATION = 200; | |
@DrawableRes private int currentIcon; | |
@StringRes private int currentText; | |
private boolean isAnimating; | |
private final MaterialButton button; | |
private final ConstraintLayout container; | |
private final Transition.TransitionListener listener = new Transition.TransitionListener() { | |
public void onTransitionStart(Transition transition) { isAnimating = true; } | |
public void onTransitionEnd(Transition transition) { isAnimating = false; } | |
public void onTransitionCancel(Transition transition) { isAnimating = false; } | |
public void onTransitionPause(Transition transition) { } | |
public void onTransitionResume(Transition transition) { } | |
}; | |
public FabIconAnimator(ConstraintLayout container) { | |
this.container = container; | |
this.button = container.findViewById(R.id.fab); | |
} | |
public void update(@DrawableRes int icon, @StringRes int text) { | |
boolean isSame = currentIcon == icon && currentText == text; | |
currentIcon = icon; | |
currentText = text; | |
animateChange(icon, text, isSame); | |
} | |
public void setExtended(boolean extended) { | |
setExtended(extended, false); | |
} | |
public void setOnClickListener(@Nullable View.OnClickListener clickListener) { | |
if (clickListener == null) { | |
button.setOnClickListener(null); | |
return; | |
} | |
AtomicBoolean flag = new AtomicBoolean(true); | |
button.setOnClickListener(view -> { | |
if (!flag.getAndSet(false)) return; | |
clickListener.onClick(view); | |
button.postDelayed(() -> flag.set(true), 2000); | |
}); | |
} | |
private boolean isExtended() { // R.dimen.triple_and_half_margin is 56 dp. | |
return button.getLayoutParams().height != button.getResources().getDimensionPixelSize(R.dimen.triple_and_half_margin); | |
} | |
private void animateChange(@DrawableRes int icon, @StringRes int text, boolean isSame) { | |
boolean extended = isExtended(); | |
button.setText(text); | |
button.setIconResource(icon); | |
setExtended(extended, !isSame); | |
if (!extended) twitch(); | |
} | |
private void setExtended(boolean extended, boolean force) { | |
if (isAnimating || (extended && isExtended() && !force)) return; | |
ConstraintSet set = new ConstraintSet(); | |
set.clone(container.getContext(), extended ? R.layout.fab_extended : R.layout.fab_collapsed); | |
TransitionManager.beginDelayedTransition(container, new AutoTransition() | |
.addListener(listener).setDuration(150)); | |
if (extended) button.setText(currentText); | |
else button.setText(""); | |
set.applyTo(container); | |
} | |
private void twitch() { | |
AnimatorSet set = new AnimatorSet(); | |
ObjectAnimator twitchA = animateProperty(ROTATION_Y_PROPERTY, TWITCH_START, TWITCH_END); | |
ObjectAnimator twitchB = animateProperty(ROTATION_Y_PROPERTY, TWITCH_END, TWITCH_START); | |
set.play(twitchB).after(twitchA); | |
set.start(); | |
} | |
@NonNull | |
private ObjectAnimator animateProperty(String property, float start, float end) { | |
return ObjectAnimator.ofFloat(container, property, start, end).setDuration(DURATION); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment