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:
Cela se produit même si le CoordinatorLayout
lui-même ne s’étend pas jusqu’à présent:
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é:
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
):
Comme auparavant, le CoordinatorView
lui-même ne s'étend que jusqu'au sommet du BottomNavigationView
.
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>
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.
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"
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.
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.