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



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

我在我的v27themes.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
}
}

另外,我正在AppBarLayout上设置android:fitsSystemWindows=true


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

我知道我需要获取WindowInsets并将其添加到我现有的填充中(或者AppBarLayout它将自行处理插图),但我无法让它与 FAB 一起使用。

我找到了这篇关于将插图添加到视图padding的文章,但由于 FAB 使用margin我不确定我是否在正确的轨道上。


是否有任何关于在边到边时如何处理插图的文档、示例和最佳实践?似乎像AppBarLayout这样的小部件可以优雅地处理它,但是我如何让FAB也调整其边距呢?


更新 1

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

我有两个布局,每个布局都有一个CoordinatorLayout:"父布局"定义了一个带有AppBarLayoutFrameLayoutCoordinatorLayout,用于保存放置在后者中的片段使用的实际内容和"子布局"。

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

编辑 2022-01-16:

如今,我们可以使用 https://google.github.io/accompanist/insets 来获取喷气背包构图的插图,只需添加WindowCompat.setDecorFitsSystemWindows(window, false),就像@shogun-nassar在他的答案中正确指出的那样。

<小时 />

原答案:

要提供最终答案,请执行以下操作:

不要在任何地方使用android:fitsSystemWindows,而是手动将插图应用于屏幕边缘的任何视图,否则这些视图会滑到系统栏后面(例如AppBarLayoutFloatingActionButton)。

我写了一些帮助程序来将插图添加到填充或边距,尊重之前添加的任何插图(需要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)将应用栏保持在状态栏下方(注意边距/填充差异)。

根据 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"

相关内容

  • 没有找到相关文章

最新更新