使用 WindowInsets 在 Android 上正确进行边到边移动

问题描述 投票:0回答:3

我正在尝试让边缘到边缘 (https://youtu.be/OCHEjeLC_UY?t=1635) 在 API 21 到 29 上正常工作。

我在我的

v27\themes.xml
上使用这个:

<item name="android:windowLightNavigationBar">true</item>
<item name="android:navigationBarColor">@android:color/transparent</item>

这在我的活动中:

override fun onWindowFocusChanged(hasFocus: Boolean) {
    super.onWindowFocusChanged(hasFocus)

    if (hasFocus) {
        window.decorView.systemUiVisibility =
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    }
}

另外,我正在我的

android:fitsSystemWindows=true
上设置
AppBarLayout


有了这个,它在 API >= 27 上看起来很好,其中内容在现在透明的导航栏后面滚动,但在较旧的 API 上,内容被黑色导航栏覆盖。

我知道我需要获取

WindowInsets
并将其添加到我现有的填充中(或者在
AppBarLayout
的情况下,它将自行处理插图),但我无法让它与 FAB 一起使用。

我发现这篇文章关于将插图添加到视图的

padding
,但因为FAB使用
margin
,我不确定我是否走在正确的轨道上。


是否有关于如何在边缘到边缘时处理插图的文档、示例、最佳实践?看起来像

AppBarLayout
这样的小部件可以很好地处理它,但是我怎样才能让 FAB 也调整其边距呢?


更新1

要说明的是,当将

android:fitsSystemWindows=true
添加到
CoordinatorLayout
时,它也可以处理插图,但有一个主要缺点:

我有两个布局,每个都有一个

CoordinatorLayout
:“父布局”定义了一个
CoordinatorLayout
,其中一个
AppBarLayout
和一个
FrameLayout
来保存实际内容和放置在后者中的片段使用的“子布局” .

因此,我无法将

android:fitsSystemWindows=true
添加到子布局中,因为这会导致顶部(工具栏和内容之间)出现空白,并且我无法将其放入父布局中,因为这样 FAB 就不会已更新至插图。

android windowinsets
3个回答
19
投票

编辑2022-01-16:

现在我们可以使用 https://google.github.io/accompanist/insets 来获取 jetpack compose 的插图,只需添加

WindowCompat.setDecorFitsSystemWindows(window, false)
就像 @shogun-nassar 在 他的答案中正确指出的那样。


原答案:

提供最终答案:

请勿在任何地方使用

android:fitsSystemWindows
,而是手动将插图应用到屏幕边缘的任何视图,否则这些视图会滑到系统栏后面(例如
AppBarLayout
FloatingActionButton
)。

我编写了一些帮助程序,将插入添加到填充或边距,尊重之前添加的任何内容(需要 androidx.core:core:1.2.0-alpha01):

fun View.addSystemWindowInsetToPadding(
    left: Boolean = false,
    top: Boolean = false,
    right: Boolean = false,
    bottom: Boolean = false
) {
    val (initialLeft, initialTop, initialRight, initialBottom) =
        listOf(paddingLeft, paddingTop, paddingRight, paddingBottom)

    ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
        view.updatePadding(
            left = initialLeft + (if (left) insets.systemWindowInsetLeft else 0),
            top = initialTop + (if (top) insets.systemWindowInsetTop else 0),
            right = initialRight + (if (right) insets.systemWindowInsetRight else 0),
            bottom = initialBottom + (if (bottom) insets.systemWindowInsetBottom else 0)
        )

        insets
    }
}

fun View.addSystemWindowInsetToMargin(
    left: Boolean = false,
    top: Boolean = false,
    right: Boolean = false,
    bottom: Boolean = false
) {
    val (initialLeft, initialTop, initialRight, initialBottom) =
        listOf(marginLeft, marginTop, marginRight, marginBottom)

    ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
        view.updateLayoutParams {
            (this as? ViewGroup.MarginLayoutParams)?.let {
                updateMargins(
                    left = initialLeft + (if (left) insets.systemWindowInsetLeft else 0),
                    top = initialTop + (if (top) insets.systemWindowInsetTop else 0),
                    right = initialRight + (if (right) insets.systemWindowInsetRight else 0),
                    bottom = initialBottom + (if (bottom) insets.systemWindowInsetBottom else 0)
                )
            }
        }

        insets
    }
}

例如,只需调用

fab.addSystemWindowInsetToMargin(bottom = true)
,FAB 就会向上移动到导航栏上方。或者
app_bar.addSystemWindowInsetToPadding(top = true)
将应用栏保持在状态栏下方(注意边距/填充差异)。


1
投票

根据https://developer.android.com/training/gestures/edge-to-edge#kotlin

只需在 BaseActivity 或任何您想要的活动上使用它即可:

   override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     WindowCompat.setDecorFitsSystemWindows(window, false)
   }

注意:请勿在任何地方使用此

android:fitsSystemWindows="true"

0
投票

Google 不能一次就把事情做对吗?仅限一次!

© www.soinside.com 2019 - 2024. All rights reserved.