J'ai une activité avec un tiroir de navigation et un fragment à fond perdu (avec une image dans le haut qui doit apparaître derrière la barre système translucide de Lollipop). Bien que j'aie eu une solution provisoire où le fragment a été gonflé simplement en ayant la balise <fragment>
dans le code XML d'Activity, cela avait l'air d'aller.
Ensuite, j'ai dû remplacer <fragment>
par <FrameLayout>
et effectuer des transactions de fragment. À présent, le fragment n'apparaît plus derrière la barre système, bien que fitsSystemWindows
soit défini sur true
dans toute la hiérarchie requise.
Je pense qu’il pourrait y avoir une différence entre la façon dont <fragment>
est gonflé dans la présentation d’Activity et par lui-même. J'ai cherché sur Google et trouvé des solutions pour KitKat, mais aucune de celles-ci n'a fonctionné pour moi (Lollipop).
activity.xml
<Android.support.v4.widget.DrawerLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:id="@+id/drawer_layout"
Android:layout_height="match_parent"
Android:layout_width="match_parent"
Android:fitsSystemWindows="true">
<FrameLayout
Android:id="@+id/fragment_Host"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true">
</FrameLayout>
<Android.support.design.widget.NavigationView
Android:id="@+id/nav_view"
Android:layout_height="match_parent"
Android:layout_width="wrap_content"
Android:layout_gravity="start"
Android:fitsSystemWindows="true"/>
</Android.support.v4.widget.DrawerLayout>
fragment.xml
<Android.support.design.widget.CoordinatorLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true">
<Android.support.design.widget.AppBarLayout
Android:layout_width="match_parent"
Android:layout_height="224dp"
Android:fitsSystemWindows="true"
Android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
...
Cela fonctionnait quand activity.xml était ainsi:
<Android.support.v4.widget.DrawerLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:id="@+id/drawer_layout"
Android:layout_height="match_parent"
Android:layout_width="match_parent"
Android:fitsSystemWindows="true">
<fragment xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:tools="http://schemas.Android.com/tools"
Android:id="@+id/fragment"
Android:name="com.actinarium.random.ui.home.HomeCardsFragment"
tools:layout="@layout/fragment_home"
Android:layout_width="match_parent"
Android:layout_height="match_parent"/>
<Android.support.design.widget.NavigationView
Android:id="@+id/nav_view"
Android:layout_height="match_parent"
Android:layout_width="wrap_content"
Android:layout_gravity="start"
Android:fitsSystemWindows="true"/>
</Android.support.v4.widget.DrawerLayout>
Lorsque vous utilisez <fragment>
, la disposition renvoyée dans la onCreateView
de votre fragment est directement attachée à la place de la balise <fragment>
(vous ne verrez jamais réellement de balise <fragment>
si vous regardez votre hiérarchie View.
Par conséquent, dans le cas <fragment>
, vous avez
DrawerLayout
CoordinatorLayout
AppBarLayout
...
NavigationView
Semblable à la façon dont cheesesquare fonctionne. Cela fonctionne parce que, comme expliqué dans cet article de blog , DrawerLayout
et CoordinatorLayout
ont tous deux des règles différentes sur la façon dont fitsSystemWindows
s'applique à eux - ils l'utilisent tous deux pour insérer des vues enfant, mais aussi call dispatchApplyWindowInsets ( ) sur chaque enfant, leur permettant d'accéder à la propriété fitsSystemWindows="true"
.
Cela diffère du comportement par défaut avec des mises en page telles que FrameLayout
où, lorsque vous utilisez fitsSystemWindows="true"
, toutes les insertions sont consommées, en appliquant aveuglément un remplissage sans informer aucune vue enfant (c'est la partie "profondeur en premier" de l'article de blog).
Ainsi, lorsque vous remplacez la balise <fragment>
par FrameLayout
et FragmentTransactions, votre hiérarchie de vues devient:
DrawerLayout
FrameLayout
CoordinatorLayout
AppBarLayout
...
NavigationView
comme la vue du fragment est insérée dans la FrameLayout
. Cette vue ne sait rien sur le passage de fitsSystemWindows
à des vues enfants. Votre CoordinatorLayout
ne verra jamais cet indicateur ni ne se comportera de manière personnalisée.
Résoudre le problème est en fait assez simple: remplacez votre FrameLayout
par un autre CoordinatorLayout
. Cela garantit que le fitsSystemWindows="true"
est transmis au CoordinatorLayout
nouvellement gonflé à partir du fragment.
Une solution alternative tout aussi valable serait de créer une sous-classe personnalisée de FrameLayout
et de remplacer onApplyWindowInsets () à distribuer à chaque enfant (dans votre cas, celui-ci) ou d’utiliser la méthode ViewCompat.setOnApplyWindowInsetsListener () intercepter l'appel en code et l'envoyer à partir de là (aucune sous-classe requise). Moins de code est généralement le plus facile à gérer, je ne recommanderais donc pas nécessairement ces itinéraires plutôt que la solution CoordinatorLayout
à moins que cela ne vous dérange vraiment.
Mon problème était similaire au vôtre: j'ai un Bottom Bar Navigation qui remplace les fragments de contenu. Maintenant, certains fragments veulent dessiner sur la barre d'état (avec CoordinatorLayout
, AppBarLayout
), d'autres pas (avec ConstraintLayout
, Toolbar
).
ConstraintLayout
FrameLayout
[the ViewGroup of your choice]
BottomNavigationView
La suggestion de ianhanniballake d’ajouter un autre calque CoordinatorLayout
n’est pas ce que je veux, j’ai donc créé une coutume FrameLayout
qui gère les incrustations (comme il l’a suggéré), et après un certain temps, je suis tombé sur cette solution qui n’a vraiment pas beaucoup de code. :
activity_main.xml
<Android.support.constraint.ConstraintLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:id="@+id/content"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<com.example.app.WindowInsetsFrameLayout
Android:id="@+id/fragment_container"
Android:layout_width="0dp"
Android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<BottomNavigationView
Android:id="@+id/bottom_navigation"
Android:layout_width="0dp"
Android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</Android.support.constraint.ConstraintLayout>
WindowInsetsFrameLayout.Java
/**
* FrameLayout which takes care of applying the window insets to child views.
*/
public class WindowInsetsFrameLayout extends FrameLayout {
public WindowInsetsFrameLayout(Context context) {
this(context, null);
}
public WindowInsetsFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WindowInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// Look for replaced fragments and apply the insets again.
setOnHierarchyChangeListener(new OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(View parent, View child) {
requestApplyInsets();
}
@Override
public void onChildViewRemoved(View parent, View child) {
}
});
}
}
OK, après que plusieurs personnes aient fait remarquer que fitsSystemWindows
fonctionnait différemment et qu'il ne devait pas être utilisé dans toutes les vues de la hiérarchie, j'ai continué à expérimenter et à supprimer la propriété de différentes vues.
J'ai obtenu l'état attendu après avoir supprimé fitsSystemWindows
de chaque noeud dans activity.xml
= \
Une autre approche écrite en Kotlin,
Le problème:
La FrameLayout
que vous utilisez ne propage pas fitsSystemWindows="true"
à ses fils:
<FrameLayout
Android:id="@+id/fragment_Host"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true" />
Une solution:
Étendez FrameLayout
class et override à la fonction onApplyWindowInsets()
pour propager les encarts de fenêtre aux fragments attachés:
@TargetApi(Build.VERSION_CODES.Lollipop)
class BetterFrameLayout : FrameLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets {
childCount.let {
// propagates window insets to children's
for (index in 0 until it) {
getChildAt(index).dispatchApplyWindowInsets(windowInsets)
}
}
return windowInsets
}
}
Utilisez cette présentation comme un conteneur de fragments au lieu de la variable standard FrameLayout
:
<com.foo.bar.BetterFrameLayout
Android:id="@+id/fragment_Host"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true" />
Supplémentaire:
Si vous voulez en savoir plus sur cet article de blog Chris Banes/ Devenir un installateur de fenêtres maître .
J'ai créé l'année dernière pour résoudre ce problème: https://Gist.github.com/cbeyls/ab6903e103475bd4d51b
Edit: assurez-vous de bien comprendre ce qui convient à SystemWindows. Lorsque vous le définissez sur une vue, cela signifie essentiellement: "placez cette vue et tous ses enfants sous la barre d'état et au-dessus de la barre de navigation". Cela n'a aucun sens de définir cet attribut sur le conteneur du haut.