在 Android N Multi-Window 模式下删除导航视图顶部填充

Removing navigation view top padding in Android N Multi-Window mode

我想从 Android N 的多 Window 模式的导航视图中删除 paddingTop/marginTop。就像 Gmail 已经做的那样。 如果您看到下图,我说的是正常填充,其大小等于导航视图开头的状态栏。

所以基本上在 Multi-Window 模式下(见下图)当我的应用程序位于屏幕的第二部分时,我必须删除该填充。

不幸的是,在新的 api 24 中,您有 isInMultiWindowMode(),但无法知道您的应用程序位于屏幕的哪一部分。

与其试图弄清楚你是否处于 multi-window 模式以及在屏幕的哪一部分,你需要让你的导航视图 header 尊重系统 windows 内嵌.

通常您只关心一个 window - 您的应用被绘制的那个。通常您甚至认为没有 windows。您的应用不是全屏绘制的吗?好吧,实际上没有。通常会有一些space保留给系统栏,比如顶部的状态栏和底部的导航栏。它们分别绘制在 windows - 系统 windows 中。 (哦,现在我们在 N 中有了 multi-window 模式。更像是 multi-app-window 模式,因为如果你算上系统 windows 那么 multi-window 已经存在了一段时间。 )

您可以让您的导航视图 header 根据它是否在系统 window 下(在本例中为状态栏)调整其插图,只需进行一些简单的调整。

假设导航视图是这样定义的:

<android.support.design.widget.NavigationView
    ...
    android:fitsSystemWindows="true"
    app:headerLayout="@layout/nav_header_main"
    ... />

并且 nav_header_main.xml 中有一个简单的 header 布局:

<?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="@dimen/nav_header_height"
    android:background="@drawable/nav_header_background"
    android:orientation="vertical"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="32dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/default_profile_picture" />

    ...

</LinearLayout>

你只需要像这样改变它:

<?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="@dimen/nav_header_height"
    android:background="@drawable/nav_header_background"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/sym_def_app_icon"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginTop="8dp"/>

    ...

</LinearLayout>
  1. 首先你需要在布局中添加android:fitsSystemWindows="true"
  2. 现在您需要缩小顶部内边距,因为 fitsSystemWindows 会自动添加状态栏大小的内边距。因此,以前您的顶部填充来自 header 的顶部,现在它仅来自状态栏的底部。
  3. 并且您必须将布局中的所有内边距移到其他位置(例如,我将它们移至 child 视图的边距),因为 fitsSystemWindows 将覆盖这些内边距。

在此之后,如果您的应用位于 multi-window 拆分的底部,则不会添加状态栏的填充。它还将使您的导航视图在不在状态栏下方的任何其他情况下正常显示,或者如果状态栏在 Android 或某些疯狂的自定义 ROM 的任何未来版本中更改大小。

对我来说没有任何效果,所以我最终选择了这条路线并完成了工作:

<android.support.design.widget.NavigationView
    ...
    app:insetForeground="@null"/>

从技术上讲,插图仍然存在,但由于用于绘制它们的 insetForeground 资源现在为空,因此 logic is skipped in ScrimInsetsFrameLayout's onDraw method(它是 NavigationView 的父级 class)。

所以当所有其他方法都失败时,这是一条相当有效的路线。