FAB не анимируется при удалении однострочного Snackbar

У меня есть CoordinatorLayout с поддержкой FloatingActionButton, в котором я хочу отображать 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>

Теперь я показываю Snackbar вот так

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

Когда он отображается, FAB скользит вверх, когда он (после LENGHT_SHORT) исчезает, FAB скользит вниз, все работает нормально.

Однако, если я смахиваю Snackbar в сторону, FAB перемещается вниз без слайд-анимации. Он просто мигает в исходное положение.

Интересно, что если Snackbar состоит из двух строк (независимо от того, есть ли у него действие или нет) и смахивается, FAB правильно возвращается на свое место с обычной анимацией слайдов .

Это ошибка в android.support.design.widget.CoordinatorLayout или android.support.design.widget.FloatingActionButton? Какое-либо обходное решение для этого?


comment
Удалось ли вам решить эту проблему?   -  person Denis    schedule 04.11.2019


Ответы (2)


Решение Kotlin без использования ValueAnimator на основе сообщения Маттео Дестро:

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()
    }
}
person GrafOrlov    schedule 22.03.2020

Недавно я столкнулся с той же проблемой, и в итоге я создал класс поведения настраиваемого макета для FAB, чтобы выполнять анимацию, когда закусочная убирается.

Создайте этот класс:

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;
    }

}

Затем в вашем макете:

 <android.support.design.widget.FloatingActionButton
      ...
      app:layout_behavior="your.package.SnackbarAwareBehavior">
person Matteo Destro    schedule 25.11.2019