web-dev-qa-db-fra.com

Afficher/masquer BottomNavigationView au défilement dans CoordinatorLayout avec AppBarLayout

J'essaie d'utiliser à la fois AppBarLayout et BottomNavigationLayout dans une seule CoordinatorLayout et j'ai du mal à cacher la BottomNavigationLayout comme l'exige la directive material .

Je veux dire quelque chose comme ça:

<Android.support.design.widget.CoordinatorLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:orientation="vertical"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="false">

    <Android.support.design.widget.AppBarLayout
        Android:id="@+id/app_bar"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        app:layout_insetEdge="top"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            app:layout_scrollFlags="scroll|enterAlways"/>
    </Android.support.design.widget.AppBarLayout>


    <Android.support.design.widget.BottomNavigationView
        Android:id="@+id/bottom_nav"
        Android:layout_width="match_parent"
        Android:layout_height="56dp"
        Android:layout_gravity="bottom"
        app:menu="@menu/menu_bottom_navigation"/>

    <FrameLayout
        Android:id="@+id/content_container"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_gravity="top"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</Android.support.design.widget.CoordinatorLayout>

Comme vous pouvez le constater, j'ai aussi une variable FrameLayout qui contient un fragment avec le contenu réel. Actuellement, il n'y a pas de comportement par défaut/intégré pour la variable BottomNavigationView - ni pour la vue elle-même, ni pour ses frères et soeurs. Le appbar_scrolling_view_behavior existant gère la vue du contenu en coordination avec la barre d’application mais ignore les autres frères et soeurs. 

Je recherche une solution pour masquer et afficher à la fois la barre d’application et la vue de navigation inférieure en défilement.

6
stan0

Après un jour ou deux de recherches, je me suis installé avec une coutume Behavior attachée à la BottomNavigationView. Son idée principale est de détecter le défilement du frère de BottomNavigationView afin qu'il puisse masquer le BottomNavigationView. Quelque chose comme ça:

public class BottomNavigationBehavior extends CoordinatorLayout.Behavior<BottomNavigationView> {

    public BottomNavigationBehavior() {
        super();
    }

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

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, BottomNavigationView child, View dependency) {
        boolean dependsOn = dependency instanceof FrameLayout;
        return dependsOn;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View directTargetChild, View target, int nestedScrollAxes) {
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, BottomNavigationView child, View target, int dx, int dy, int[] consumed) {
        if(dy < 0) {
            showBottomNavigationView(child);
        }
        else if(dy > 0) {
            hideBottomNavigationView(child);
        }
    }

    private void hideBottomNavigationView(BottomNavigationView view) {
        view.animate().translationY(view.getHeight());
    }

    private void showBottomNavigationView(BottomNavigationView view) {
        view.animate().translationY(0);
    }
}

Comme vous pouvez le constater, j'utilise la méthode ViewPropertyAnimator simple, obtenue à l'aide de la méthode animate de la vue enfant. Cela conduit à une animation simple qui ne correspond pas vraiment au comportement de AppBarLayout mais qui est suffisamment décente pour paraître belle et en même temps assez simple pour être mise en œuvre.

Je pense qu’à un moment donné, l’équipe Android ajoutera un comportement par défaut pour BottomNavigationView dans la bibliothèque de support technique. Par conséquent, je ne pense pas qu’il soit raisonnable d’investir plus de temps pour dupliquer exactement le comportement de AppBarLayout.

edit (avril 2018): voir la section des commentaires pour une clarification mineure à propos de onStartNestedScroll et onNestedPreScroll et de leurs nouvelles versions.

20
stan0

Vous pouvez également utiliser HideBottomViewOnScrollBehavior . Ce comportement fonctionne fondamentalement de la même manière, mais gère également l'annulation de toutes les animations existantes en cours d'exécution qui devraient être meilleures pour la performance.

2
Cameron Ketcham