Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FAB not animated when one-line Snackbar is swiped away

I have a CoordinatorLayout with support FloatingActionButton in which I want to display a Snackbar..

<android.support.design.widget.CoordinatorLayout
    android:id="@+id/rootView"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        ...
    </RelativeLayout>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_add_field"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/margin_default"
        android:layout_marginBottom="@dimen/profile_floating_margin_bottom"
        android:layout_marginRight="@dimen/profile_floating_margin_right"
        android:clickable="true"
        android:src="@drawable/icon_plus"
        app:backgroundTint="@color/brand_green"
        app:borderWidth="0dp"
        app:elevation="@dimen/fac_icon_elevation"/>
</android.support.design.widget.CoordinatorLayout>

Now I'm showing the Snackbar like this

Snackbar.make(mRootView, "Something went wrong", Snackbar.LENGHT_SHORT)
        .show();

When it's displayed, FAB slides up, when it (after LENGHT_SHORT) disappears, FAB slides down, all working fine.

However, if I swipe the Snackbar away, FAB moves down without the slide animation. It just flashes to its initial position.

Interestingly, if the Snackbar has two lines (no matter if it has an action or not) and is swiped away, FAB animates back to its place correctly with the usual slide animation.

Is that a bug in android.support.design.widget.CoordinatorLayout or android.support.design.widget.FloatingActionButton? Any workaround for this?

like image 646
Marcel Bro Avatar asked Apr 08 '16 10:04

Marcel Bro


2 Answers

Kotlin solution without using of a ValueAnimator based on Matteo Destro's post:

class SnackBarAwareBehavior : CoordinatorLayout.Behavior<View> {

    companion object {
        private const val DURATION = 180
    }

    constructor() : super() {}

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}

    override fun layoutDependsOn(
        parent: CoordinatorLayout,
        child: View,
        dependency: View
    ): Boolean {
        return dependency is Snackbar.SnackbarLayout
    }

    override fun onDependentViewChanged(
        parent: CoordinatorLayout,
        child: View,
        dependency: View
    ): Boolean {
        if (dependency.visibility == View.VISIBLE) {
            moveChildUp(child, dependency.height + dependency.marginBottom)
            return true
        }
        return false
    }

    override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
        moveChildToInitialPosition(child)
    }

    private fun moveChildUp(child: View, translation: Int) {
        child.animate()
            .translationY((-translation).toFloat())
            .setInterpolator(DecelerateInterpolator())
            .setDuration(DURATION.toLong())
            .start()
    }

    private fun moveChildToInitialPosition(child: View) {
        child.animate()
            .translationY(0f)
            .setInterpolator(AccelerateInterpolator())
            .setDuration(DURATION.toLong())
            .start()
    }
}
like image 156
GrafOrlov Avatar answered Nov 14 '22 22:11

GrafOrlov


I've encountered the same problem recently and I've ended up creating a custom layout behavior class for the FAB to execute an animation when the snackbar is swiped away.

Create this class:

public class SnackbarAwareBehavior extends CoordinatorLayout.Behavior<View> {

    private static final int ANIMATION_DURATION = 180;

    public SnackbarAwareBehavior() {
        super();
    }

    public SnackbarAwareBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
        return dependency instanceof Snackbar.SnackbarLayout;
    }

    @Override
    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
        if (dependency.getVisibility() == View.VISIBLE) {
            // Change the translation of the FAB only if the snackbar is visible
            child.setTranslationY(dependency.getTranslationY() - getTranslationYBottom(dependency));
            return true;
        }
        return false;
    }

    @Override
    public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
        if (dependency.getVisibility() == View.VISIBLE) {
            child.setTranslationY(0f);
        } else {
            // Execute the animation only if the snackbar has been swiped away, i.e. when it's not visible
            ValueAnimator animator = new ValueAnimator();
            animator.setFloatValues(child.getTranslationY(), 0f);
            animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
            animator.setDuration(ANIMATION_DURATION);
            animator.addUpdateListener(animation -> child.setTranslationY((float) animation.getAnimatedValue()));
            animator.start();
        }
    }

    private int getTranslationYBottom(View view) {
        // Get the translation position of the snackbar when it's hidden.
        // Method taken from BaseTransientBottomBar.Behavior class.
        int translationY = view.getHeight();
        ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
        if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
            translationY += ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin;
        }
        return translationY;
    }

}

Then in your layout:

 <android.support.design.widget.FloatingActionButton
      ...
      app:layout_behavior="your.package.SnackbarAwareBehavior">
like image 2
Matteo Destro Avatar answered Nov 14 '22 22:11

Matteo Destro