Setting Statusbar padding to NavigationView in Android
Solution 1
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:headerLayout="@layout/nav_header"
app:menu="@menu/menu_navigation"
android:fitsSystemWindows="false"
android:layout_gravity="start"/>
android:fitsSystemWindows="false"
and CoordinatorLayout => android:fitsSystemWindows="false"
Solution 2
First, remember that you cannot draw behind status bar before Lollipop.
Solution
For Lollipop+, you should set the android:fitsSystemWindows="true"
for DrawerLayout
and leave the default for NavigationView
.
Then on your activity or fragment after setting the header:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
navigationView.setOnApplyWindowInsetsListener { v, insets ->
val header = navigationView.getHeaderView(0)
header.setPadding(
header.paddingLeft,
header.paddingTop + insets.systemWindowInsetTop,
header.paddingRight,
header.paddingBottom
)
insets.consumeSystemWindowInsets()
}
}
Explanation
The window is passing a object called insets
(with dimensions for top, left, bottom, right) to it's childs views. The child views that have the fitSystemWindows=true
will do nothing with this insets and may send it forward to its children. The views that have it false may use the values to add extra padding or margin to itself.
Set fitSystemWindows=false
of the NavigationView
is not working because it is using the insets to apply extra margin to itself. What you want is your header view LinearLayout
to apply this margin. But there is two intermediate container views inside NavigationView
that will not pass forward the insets to your LinearLayout
, so you have to intercept the windows inset before these containers and apply yourself the margin or padding of your header view.
What you should never do
Never assume that the window insets will be a fixed value or get them from resources, no matter what the Android version is. Specific devices have specific values for window insets, some have thicker "notches" to fit big cameras, some manufacturers decide to make it thinner than Android recommended specs.
Solution 3
You're using LinearLayout
as the root element of your header layout. FrameLayout
, LinearLayout
, etc. are basic layouts that ignore the android:fitsSystemWindows
attribute. I recommend you to use CoordinatorLayout
as your header layout's root element. Using CoordinatorLayout
with its android:fitsSystemWindows
attribute set to true
will automatically adjust its internal padding to prevent its child elements from being overlapped by system windows such as the status bar.
These info are from this article, "Why would I want to fitsSystemWindows?" by Ian Lake.
While the basic layouts (FrameLayout, LinearLayout, etc) use the default behavior, there are a number of layouts that already customize how they react to fitsSystemWindows to fit specific use cases.
...
CoordinatorLayout also takes advantage of overriding how it handles window insets, allowing the Behavior set on child Views to intercept and change how Views react to window insets, before calling dispatchApplyWindowInsets() on each child themselves. It also uses the fitsSystemWindows flag to know if it needs to paint the status bar background.
To achieve what you want, make sure that you follow this layout structure.
layout/activity_xxxxx.xml
<android.support.v4.widget.DrawerLayout
...
android:fitsSystemWindows="true">
<!-- content -->
...
<!-- drawer -->
<android.support.design.widget.NavigationView
...
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header" />
<android.support.v4.widget.DrawerLayout>
layout/nav_header.xml
<android.support.design.widget.CoordinatorLayout
...
android:fitsSystemWindows="true">
<!-- content -->
...
<android.support.design.widget.CoordinatorLayout>
Sreekanth
Updated on June 17, 2022Comments
-
Sreekanth almost 2 years
I have an activity which hosts a DrawerLayout and NavigationView from support library. I'm setting a header layout to the navigation view and I want the navigation header height to be "wrap_content". So when I set the height to "wrap_content" the header layout goes behind the status bar.
The result I want is that the navigation drawer should draw behind the status bar but the navigation header should be pushed down by the statusbar height.
Below is the screenshot of what I am getting. Note the "SIGN IN" button going behind the status bar.
Activity layout
<android.support.v4.widget.DrawerLayout 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:id="@+id/nav_drawer" android:fitsSystemWindows="true"> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_behavior="@string/appbar_scrolling_view_behavior"></android.support.v4.view.ViewPager> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <include layout="@layout/include_toolbar"/> <android.support.design.widget.TabLayout app:theme="@style/ThemeOverlay.AppCompat.Dark" style="@style/MyCustomTabLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/tabs" /> </android.support.design.widget.AppBarLayout> </android.support.design.widget.CoordinatorLayout> <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="wrap_content" android:layout_height="match_parent" app:headerLayout="@layout/nav_header" app:menu="@menu/menu_navigation" android:fitsSystemWindows="true" android:layout_gravity="start"/> </android.support.v4.widget.DrawerLayout>
Navigation View Header Layout
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimaryDark" android:padding="16dp" android:theme="@style/ThemeOverlay.AppCompat.Dark" android:orientation="vertical" android:fitsSystemWindows="true" android:gravity="bottom"> <TextView android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/text_user_name" android:textAppearance="@style/TextAppearance.AppCompat.Body1"/> <TextView android:visibility="gone" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/text_email" android:textAppearance="@style/TextAppearance.AppCompat.Body2"/> <Button android:id="@+id/button_sign_in" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Sign In"/> </LinearLayout>
I've searched through StackOverflow for a solution but couldn't find it. So someone please shed some light. Thanks in advance.