Настройка заполнения строки состояния для NavigationView в Android
У меня есть деятельность, которая содержит DrawerLayout и NavigationView из библиотеки поддержки. Я устанавливаю макет заголовка для вида навигации и хочу, чтобы высота заголовка навигации была "wrap_content". Поэтому, когда я устанавливаю высоту на "wrap_content", макет заголовка идет за строкой состояния.
В результате я хочу, чтобы панель навигации отображалась за строкой состояния, а заголовок навигации должен быть опущен на высоту строки состояния.
Ниже скриншот того, что я получаю. Обратите внимание на кнопку "ВХОД", расположенную за строкой состояния.
Макет деятельности
<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>
Вид заголовка навигации
<?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>
Я искал решение в Stackru, но не смог его найти. Так что кто-то, пожалуйста, пролить немного света. Заранее спасибо.
3 ответа
<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"/>
андроид:fitsSystemWindows="ложь"
и CoordinatorLayout => android:fitsSystemWindows="false"
Возможным решением является использование dimens.xml
:
Заголовок для API 19 и ниже: res /values/dimens.xml
<resources>
....
<dimen name="navigation_header_height">150dp</dimen>
<dimen name="navigation_header_top_padding">0dp</dimen>
</resources>
Заголовок для API 21 и выше: res /values-v21/dimens.xml
<resources>
....
<dimen name="navigation_header_height">174dp</dimen>
<dimen name="navigation_header_top_padding">24dp</dimen>
</resources>
Пример заголовка: res /layout/nav_header.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="240dp"
android:layout_height="@dimen/navigation_header_height"
android:paddingTop="@dimen/navigation_header_top_padding"
android:background="@color/primary"
android:orientation="vertical">
// Header's views goes here
</LinearLayout>
Во-первых, помните, что вы не можете рисовать за строкой состояния до Lollipop.
Решение
Для Lollipop+, вы должны установить android:fitsSystemWindows="true"
за DrawerLayout
и оставить значение по умолчанию для NavigationView
,
Затем по вашей деятельности или фрагменту после установки заголовка:
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()
}
}
объяснение
Окно передает объект с именем insets
(с размерами сверху, слева, снизу, справа) до дочерних видов. Дочерние взгляды, которые имеют fitSystemWindows=true
ничего не сделает с этими вставками и может отправить его своим детям. Представления, которые имеют значение false, могут использовать значения для добавления дополнительных отступов или полей.
Задавать fitSystemWindows=false
из NavigationView
не работает, потому что он использует вставки, чтобы применить дополнительный запас к себе. То, что вы хотите, это ваш вид заголовка LinearLayout
применить эту маржу. Но есть два промежуточных вида контейнера внутри NavigationView
что не передаст вставки к вашему LinearLayout
Таким образом, вы должны перехватить вставку окон перед этими контейнерами и самостоятельно применить поля или отступы вашего заголовка.
Что ты никогда не должен делать
Никогда не предполагайте, что вставки окон будут фиксированными или получат их из ресурсов, независимо от версии Android. Определенные устройства имеют определенные значения для оконных вставок, некоторые имеют более толстые "выемки", чтобы соответствовать большим камерам, некоторые производители решили сделать его тоньше, чем рекомендуемые Android спецификации.
Вы используете LinearLayout
как корневой элемент макета вашего заголовка. FrameLayout
, LinearLayout
и т. д. являются основными макетами, которые игнорируют android:fitsSystemWindows
приписывать. Я рекомендую вам использовать CoordinatorLayout
как корневой элемент макета вашего заголовка. С помощью CoordinatorLayout
с этими android:fitsSystemWindows
атрибут установлен в true
автоматически настроит свой внутренний отступ, чтобы его дочерние элементы не перекрывались системными окнами, такими как строка состояния.
Эта информация взята из этой статьи "Почему я хочу использовать SystemWindows?" Яном Лейком.
Хотя базовые макеты (FrameLayout, LinearLayout и т. Д.) Используют поведение по умолчанию, существует ряд макетов, которые уже настраивают свою реакцию на fitsSystemWindows для соответствия конкретным случаям использования.
...
CoordinatorLayout также использует преимущества переопределения того, как он обрабатывает вставки окна, позволяя параметру Поведение, установленному в дочерних представлениях, перехватывать и изменять то, как представления реагируют на вставки окна, перед вызовом dispatchApplyWindowInsets() для каждого дочернего элемента. Он также использует флаг fitsSystemWindows, чтобы узнать, нужно ли рисовать фон строки состояния.
Чтобы добиться того, что вы хотите, убедитесь, что вы следуете этой структуре макета.
макет /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>
макет /nav_header.xml
<android.support.design.widget.CoordinatorLayout
...
android:fitsSystemWindows="true">
<!-- content -->
...
<android.support.design.widget.CoordinatorLayout>