web-dev-qa-db-fra.com

L'enfant de contenu CoordinatorLayout chevauche BottomNavigationView

J'essaie d'utiliser un CoordinatorLayout avec un BottomNavigationView, un AppBarLayout et un ViewPager. Voici ma mise en page:

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:fitsSystemWindows="true"
        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:layout_scrollFlags="enterAlways|scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

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

    <Android.support.v4.view.ViewPager
        Android:id="@+id/pager"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <Android.support.design.widget.BottomNavigationView
        Android:id="@+id/navigation"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_gravity="bottom"
        Android:background="?android:attr/windowBackground"
        app:itemIconTint="?colorPrimaryDark"
        app:itemTextColor="?colorPrimaryDark"
        app:menu="@menu/navigation"/>
</Android.support.design.widget.CoordinatorLayout>

Le problème est que CoordinatorLayout place le ViewPager à s’étendre au bas de l’écran, de sorte que le bas est obscurci par le BottomNavigationView, comme ceci:

 bounds of ViewPager

Cela se produit même si le CoordinatorLayout lui-même ne s’étend pas jusqu’à présent:

 bounds of CoordinatorLayout

J'ai essayé d'ajouter app:layout_insetEdge="bottom" à BottomNavigationView et app:layout_dodgeInsetEdges="bottom" à ViewPager, mais le problème est différent: il déplace le bas de ViewPager vers le haut, mais il conserve la même hauteur. Le sommet est alors coupé:

 shifted ViewPager bounds

J'ai essayé deux autres expériences. Premièrement, j'ai essayé de supprimer BottomNavigationView de CoordinatorLayout et de les rendre frères et soeurs sous un LinearLayout vertical. Deuxièmement, je place les ViewPager et BottomNavigationView ensemble sous un LinearLayout, en espérant qu’ils seraient correctement disposés. Ni l'un ni l'autre n'a aidé: dans le premier cas, CoordinatorLayout dimensionnait toujours ViewPager par rapport à tout l'écran, en cachant une partie derrière l'écran BottomNavigationView ou en coupant le haut de l'écran. Dans le second cas, l'utilisateur doit faire défiler l'écran pour afficher BottomNavigationView.

Comment puis-je obtenir la bonne mise en page?

P.S. Lorsque j'ai essayé la mise en page suggérée par @Anoop S S (en plaçant CoordinatorLayout et BottomNavigationView comme frères et sœurs sous un RelativeLayout), le message suivant (avec le ViewPager toujours étendu derrière le BottomNavigationView):

 result of Anoop's layout

Comme auparavant, le CoordinatorView lui-même ne s'étend que jusqu'au sommet du BottomNavigationView.

7
Ted Hopp

Fondamentalement, vous devez créer un Relativelayout en tant que parent et mettre BottomNavigationView et CoordinatorLayout en tant qu'enfants. Alignez ensuite BottomNavigationView en bas et définissez CoordinatorLayout au-dessus. S'il vous plaît essayez le code ci-dessous. Il pourrait avoir peu d’attributs erros, parce que je l’ai écrit ici même. Et désolé pour l'indentation foirée.

<?xml version="1.0" encoding="utf-8"?>

    <RelativeLayout
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res-auto"
        xmlns:tools="http://schemas.Android.com/tools"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        tools:context=".MainActivity">

    <Android.support.design.widget.CoordinatorLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_above="@+id/navigation"
        >

        <Android.support.design.widget.AppBarLayout
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:fitsSystemWindows="true"
            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:layout_scrollFlags="enterAlways|scroll"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

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

        <Android.support.v4.view.ViewPager
            Android:id="@+id/pager"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

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

<Android.support.design.widget.BottomNavigationView
            Android:id="@+id/navigation"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_alignParentBottom="true"
            Android:background="?android:attr/windowBackground"
            app:itemIconTint="?colorPrimaryDark"
            app:itemTextColor="?colorPrimaryDark"
            app:menu="@menu/navigation"/>

    </RelativeLayout>
2
Anoop S S

J'ai eu un problème similaire avec une mise en page très proche de OP et un ViewPager avec 3 pages mais seulement les pages 2 et 3 qui devraient être affectées par appbar_scrolling_view_behavior.

Après avoir lutté pendant des heures à explorer des solutions sans issue (layout_dodgeInsetEdges, incrustations de fenêtre, tentative de modification de la taille mesurée de la page de ViewPager, Android: clipChildren, fitSystemWindows, ...), j'ai finalement trouvé une solution simple, détaillée ci-dessous.

Comme Vin Norman l'a expliqué, le chevauchement de BottomNavigation dans ViewPager est entièrement causé par appbar_scrolling_view_behavior défini sur le ViewPager. AppBarLayout fera simplement passer en plein écran le frère qui a appbar_scrolling_view_behavior. Ça fonctionne comme ça. 

Si vous n'avez besoin de ce comportement que sur certaines pages ViewPager, il existe un correctif simple que vous pouvez appliquer sur OnPageChangeListener du ViewPager pour modifier de manière dynamique le comportement et ajouter/supprimer le remplissage requis:

public class MyOnPageChangeListener extends  ViewPager.SimpleOnPageChangeListener {

        @Override
        public void onPageSelected(int position) {

           ...

           CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) _viewPager.getLayoutParams();

        if(position == 0) {
            params.setBehavior(null);
            params.setMargins(params.leftMargin, _appBarLayoutViewPagerMarginTopPx, 
                    params.rightMargin, _appBarLayoutViewPagerMarginBottomPx);
        } else {
            params.setBehavior(_appBarLayoutViewPagerBehavior);
            params.setMargins(params.leftMargin, 0, params.rightMargin, 0);
        }

        _viewPager.requestLayout();

        }

}

Pour la page située à la position 0 (celle que nous souhaitons que le ViewPager s’étende exactement au-dessous de la barre d’outils et au-dessus de BottomNavigationView), il supprime le comportement et ajoute un remplissage supérieur et inférieur, respectivement _appBarLayoutViewPagerMarginTopPx et _appBarLayoutViewPagerMarginBottomPx qui sont des constantes faciles à calculer auparavant (respectivement la valeur en pixel pour R.attr.actionbarSize et la hauteur pour NavigationBottomView (généralement 56dp)

Pour toutes les autres pages nécessitant appbar_scrolling_view_behavior, nous restaurons le comportement de défilement associé (préalablement stocké dans _appBarLayoutViewPagerBehavior) et supprimons le remplissage supérieur et inférieur.

J'ai testé cette solution et cela fonctionne bien sans réserve.

1
b0b

Je suis venu avec une approche différente (pas encore testée):

Je sous-classe AppBarLayout.ScrollingViewBehavior pour ajuster la marge inférieure de la vue de contenu en fonction de la hauteur de la BottomNavigationView (si présente). De cette façon, il devrait être futur (si tout va bien) si la hauteur de BottomNavigationView change pour une raison quelconque.

La sous-classe (Kotlin):

class ScrollingViewWithBottomNavigationBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.ScrollingViewBehavior(context, attrs) {
    // We add a bottom margin to avoid the bottom navigation bar
    private var bottomMargin = 0

    override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        return super.layoutDependsOn(parent, child, dependency) || dependency is BottomNavigationView
    }

    override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        val result = super.onDependentViewChanged(parent, child, dependency)

        if(dependency is BottomNavigationView && dependency.height != bottomMargin) {
            bottomMargin = dependency.height
            val layout = child.layoutParams as CoordinatorLayout.LayoutParams
            layout.bottomMargin = bottomMargin
            child.requestLayout()
            return true
        } else {
            return result
        }
    }
}

Et puis, dans la mise en page XML, vous mettez:

app:layout_behavior=".ScrollingViewWithBottomNavigationBehavior"

au lieu de

app:layout_behavior="@string/appbar_scrolling_view_behavior"
1
James

Ceci est causé par app:layout_behavior="@string/appbar_scrolling_view_behavior" dans votre ViewPager. Si vous supprimez cette ligne, vous verrez maintenant qu'elle correspond au conteneur CoordinatorLayout (malheureusement, cela inclut maintenant d'être sous la barre d'outils).

J'ai trouvé qu'il était utile de traiter CoordinatorLayout comme un simple FrameLayout, avec quelques astuces supplémentaires. L’attribut app: layout_behavior ci-dessus est nécessaire pour permettre à la barre d’outils de défiler. En réalité, la mise en page le fait en faisant en sorte que la vue liée à la barre d’outil en réduction (dans votre cas, votre ViewPager) La hauteur de la barre d'outils est supérieure aux limites. Faire défiler vers le haut amène la vue vers le bas à l'intérieur des limites et pousse la barre d'outils vers le haut, au-delà des limites. Défilement vers le bas, vice versa.

Maintenant, sur le BottomNavigationView! Si, comme je l'ai fait, vous souhaitez que BottomNavigationView soit visible en permanence, déplacez-le en dehors de CoordinatorLayout, comme l'a dit Anoop. Utilisez CoordinatorLayout uniquement pour les éléments devant être coordonnés, tout le reste à l'extérieur. Il m'est arrivé d'utiliser un objet ConstraintLayout pour ma vue parent (vous pouvez utiliser RelativeLayout ou tout ce qui fonctionne pour vous). Avec ConstraintLayout, cela ressemblerait pour vous à ceci:

 <Android.support.constraint.ConstraintLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true">


<Android.support.design.widget.CoordinatorLayout
    Android:layout_width="0dp"
    Android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:context=".MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:fitsSystemWindows="true"
        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:layout_scrollFlags="enterAlways|scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

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

    <Android.support.v4.view.ViewPager
        Android:id="@+id/pager"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

<Android.support.design.widget.BottomNavigationView
    Android:id="@+id/navigation"
    Android:layout_width="0dp"
    Android:layout_height="wrap_content"
    Android:background="?android:attr/windowBackground"
    app:itemIconTint="?colorPrimaryDark"
    app:itemTextColor="?colorPrimaryDark"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:menu="@menu/navigation" />

 </Android.support.constraint.ConstraintLayout>

En mode Conception d'Android Studio, le ViewPager semble toujours plus volumineux que le conteneur (vous avez probablement l'impression qu'il se trouve toujours derrière la barre de navigation inférieure). Mais ce n’est pas grave, lorsque vous arrivez au bas du contenu du ViewPager, cela s’affiche (c’est-à-dire que vous ne serez pas derrière la navigation du bas). Cette bizarrerie dans la vue Conception est simplement la manière dont CoordinatorLayout fait afficher/masquer la barre d'outils, comme mentionné précédemment.

0
Vin Norman

Si cela compte toujours pour quelqu'un:

Dans la réponse d'Anoop SS ci-dessus, essayez de remplacer RelativeLayout par LinearLayout. Définissez également layout_height of CoordinatorLayout sur 0dp et définissez layout_weight sur 1.

J'ai eu presque le même problème ... juste que je voulais avoir un AdView statique au bas au lieu du BottomNavigationView. En essayant les suggestions d'Anoop SS, au début, j'ai eu le même comportement qu'OP: ViewPager étendu derrière le AdView. Mais ensuite, j'ai fait ce que j'avais suggéré et tout a bien fonctionné.

Les mises en page Android se comportent de manière étrange ou peuvent être le manque de bonne documentation ou le manque de connaissances de notre part ... mais faire une mise en page est tout simplement trop ennuyeux la plupart du temps.

0
Shahood ul Hassan