什么是WindowInsets?

Sam*_*try 18 java android android-layout android-view windowinsets

我正在尝试了解Android操作系统,当我阅读Google I/O 2014应用程序时,我遇到了WindowInsets.如果有人能解释他们是什么,那将是一个很大的帮助.谢谢.

azi*_*ian 63

WindowInsets 是应用于窗口的系统视图(例如状态栏,导航栏)的插入(或大小).

在具体的例子上很容易理解.想象这个场景:

在此输入图像描述

现在,您不希望WindowInsets应用于背景ImageView,因为在这种情况下,ImageView将按状态栏高度填充.

但是你确实希望将insets应用于Toolbar,否则Toolbar将被绘制在状态栏的中间位置.

该视图声明希望WindowInsets在xml中应用:

android:fitsSystemWindows="true"
Run Code Online (Sandbox Code Playgroud)

在此示例中,您无法应用WindowInsets根布局,因为根布局将消耗WindowInsets,并且ImageView将填充.

相反,您可以使用ViewCompat.setOnApplyWindowInsetsListener将insets应用于工具栏:

ViewCompat.setOnApplyWindowInsetsListener(toolbar, (v, insets) -> {
            ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).topMargin =
                    insets.getSystemWindowInsetTop();
            return insets.consumeSystemWindowInsets();
        });
Run Code Online (Sandbox Code Playgroud)

注意,当Toolbar根布局传递WindowsInsets给它的子节点时,将调用此回调.布局喜欢FrameLayout,LinearLayout不喜欢DrawerLayout,CoordinatorLayout做.

您可以子类化您的布局,例如FrameLayout并覆盖onApplyWindowInsets:

@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    int childCount = getChildCount();
    for (int index = 0; index < childCount; index++)
        getChildAt(index).dispatchApplyWindowInsets(insets); // let children know about WindowInsets

    return insets;
}
Run Code Online (Sandbox Code Playgroud)

在Ian Lake的媒体上有一篇很好的博客文章关于这些东西,还有Chris Banes的"成为主窗口钳工"的演讲.

我还在Medium关于WindowInsets 创建了一篇详细的文章.

更多资源:


Val*_*kov 6

Android 系统使用屏幕的某些部分来呈现自己的内容,例如顶部的状态栏和底部的导航栏。例如,如果一个应用程序想要在底部栏后面呈现,它应该考虑底部栏占用的区域,否则应用程序 UI 将与系统 UI 冲突,你会得到这样的东西¹:

隐藏在底部栏后面的 FAB 按钮示例

在上面的图像上,应将额外的底部边距添加到 FAB 按钮,以便按钮不会与底部栏相交。WindowInsets API允许您获取系统 UI 使用的底部插图等信息。您经常会遇到fitsSystemWindows用于类似目的的属性,请参阅此答案以获取有关该属性的更多信息以及何时应该使用它而不是WindowInsets. 你也可以看看这篇很棒的文章:手势导航:处理视觉重叠(二)。等等,什么是手势导航

好吧,视觉重叠并不是您可能面临的唯一问题。自 Android 10 (API 29) 起,添加了新的手势导航模式。现在,用户可以选择使用手势在应用程序之间导航,而不是像上图那样使用按钮栏。现在强烈建议应用程序在导航栏后面绘制,以便用户拥有更现代的用户体验。但除此之外,还引入了一种新的插图类型 - 手势插图。事实证明,如果选择了手势导航模式,应用程序手势可能会与系统手势发生冲突。例如,让我们看看下面的图像²:

与系统手势冲突的搜索栏示例

如您所见,搜索栏离底部边缘太近,与系统快速切换手势冲突。由于系统手势具有更高的优先级,因此搜索栏变得无法使用。手势导航:处理手势冲突(三)一文中很好地描述了此示例以及其他常见场景。

我上面提到的文章是由在 Android 团队工作的Chris Banes撰写的手势导航系列的一部分。如果您想更深入地了解主题,我建议您阅读整个系列。另一篇文章动画你的键盘(第 1 部分)也可能有帮助,它描述了 WindowInsets API 的持续变化以及新的 IME 插入类型。

参考:


¹图片取自Gesture Navigation: going edge-to-edge (I)文章

²图片来自Gesture Navigation:处理手势冲突(三)


Hen*_*nry 5

您可以在此处 了解有关WindowInsets的全部信息WindowInsets为您提供应用程序在窗口上可用的区域。就其本身而言,它没有多大用处。当您重写View.onApplyWindowInsets或实现时,这才是真正的目的View.OnApplyWindowInsetsListener。您可以在此处阅读有关它们的信息:View.onApplyWindowInsetsView.OnApplyWindowInsetsListener

用于以自定义方式在视图上应用窗口插图的侦听器。

如果应用程序想要将自定义策略应用于视图的窗口插入方式,则可以选择实现此接口。如果设置了OnApplyWindowInsetsListener,则将调用其onApplyWindowInsets方法,而不是视图自身的onApplyWindowInsets方法。侦听器可以选择调用参数View的onApplyWindowInsets方法,以将View的正常行为作为其自身的一部分来应用。

简而言之,重写此设置将使您可以控制可用于View的窗口区域。