I have an Activity with navigation drawer and full-bleed Fragment (with image in the top that must appear behind translucent system bar on Lollipop). While I had an interim solution where the Fragment was inflated by simply having <fragment>
tag in Activity's XML, it looked fine.
Then I had to replace <fragment>
with <FrameLayout>
and perform fragment transactions, and now the fragment does not appear behind the system bar anymore, despite fitsSystemWindows
is set to true
across all required hierarchy.
I believe there might be some difference between how <fragment>
gets inflated within Activity's layout vs on its own. I googled and found some solutions for KitKat, but neither of those worked for me (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">
...
It worked when activity.xml was this way:
<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>
OK, after several people pointing out that
fitsSystemWindows
works differently, and it should not be used on every view down the hierarchy, I went on experimenting and removing the property from different views.I got the expected state after removing
fitsSystemWindows
from every node inactivity.xml
=\My problem was similar to yours: I have a Bottom Bar Navigation which is replacing the content fragments. Now some of the fragments want to draw over the status bar (with
CoordinatorLayout
,AppBarLayout
), others not (withConstraintLayout
,Toolbar
).The suggestion of ianhanniballake to add another
CoordinatorLayout
layer is not what I want, so I created a customFrameLayout
which handles the insets (like he suggested), and after some time I came upon this solution which really is not much code:activity_main.xml
WindowInsetsFrameLayout.java
When you use
<fragment>
, the layout returned in your Fragment'sonCreateView
is directly attached in place of the<fragment>
tag (you'll never actually see a<fragment>
tag if you look at your View hierarchy.Therefore in the
<fragment>
case, you haveSimilar to how cheesesquare works. This works because, as explained in this blog post,
DrawerLayout
andCoordinatorLayout
both have different rules on howfitsSystemWindows
applies to them - they both use it to inset their child Views, but also call dispatchApplyWindowInsets() on each child, allowing them access to thefitsSystemWindows="true"
property.This is a difference from the default behavior with layouts such as
FrameLayout
where when you usefitsSystemWindows="true"
is consumes all insets, blindly applying padding without informing any child views (that's the 'depth first' part of the blog post).So when you replace the
<fragment>
tag with aFrameLayout
and FragmentTransactions, your view hierarchy becomes:as the Fragment's view is inserted into the
FrameLayout
. That View doesn't know anything about passingfitsSystemWindows
to child views, so yourCoordinatorLayout
never gets to see that flag or do its custom behavior.Fixing the problem is actually fairly simple: replace your
FrameLayout
with anotherCoordinatorLayout
. This ensures thefitsSystemWindows="true"
gets passed onto the newly inflatedCoordinatorLayout
from the Fragment.Alternate and equally valid solutions would be to make a custom subclass of
FrameLayout
and override onApplyWindowInsets() to dispatch to each child (in your case just the one) or use the ViewCompat.setOnApplyWindowInsetsListener() method to intercept the call in code and dispatch from there (no subclass required). Less code is usually the easiest to maintain, so I wouldn't necessarily recommend going these routes over theCoordinatorLayout
solution unless you feel strongly about it.Another approach written in Kotlin,
The problem:
The
FrameLayout
you are using does not propagatefitsSystemWindows="true"
to his childs:A solution:
Extend
FrameLayout
class and override the functiononApplyWindowInsets()
to propagate the window insets to attached fragments:Use this layout as a fragment container instead of the standard
FrameLayout
:Extra:
If you want to know more about this checkout Chris Banes blog post Becoming a master window fitter.
I created this last year to solve this problem: https://gist.github.com/cbeyls/ab6903e103475bd4d51b
Edit: be sure you understand what fitsSystemWindows does first. When you set it on a View it basically means: "put this View and all its children below the status bar and above the navigation bar". It makes no sense to set this attribute on the top container.