使用 WindowInsets 在 Android 上正确地进行边到边
Go edge-to-edge on Android correctly with WindowInsets
我正在尝试让边缘到边缘 (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>
这是我的 Activity:
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
的情况下它将自行处理插图),但我无法让它工作有工厂。
我发现 this article 关于将插图添加到视图的 padding
但因为 FAB 使用 margin
我不确定,如果我在正确的轨道上.
是否有关于如何处理边到边插入的文档、示例和最佳实践?似乎 AppBarLayout
等一些小部件可以优雅地处理它,但我怎样才能让 FAB 也调整它的边距?
更新 1
具体来说,当将 android:fitsSystemWindows=true
添加到 CoordinatorLayout
时,它也会处理插图,但有一个主要缺点:
我有两个布局,每个布局都有一个 CoordinatorLayout
: "parent layout" 定义了一个 CoordinatorLayout
和一个 AppBarLayout
和一个 FrameLayout
来保存实际内容"child layout" 被放置在后者中的 Fragment 使用。
因此,我无法将 android:fitsSystemWindows=true
添加到子布局,因为它会导致顶部(工具栏和内容之间)出现空白 space,而且我无法将其放入父布局,因为 FAB 不会更新到插图。
编辑 2022-01-16:
现在我们可以使用 https://google.github.io/accompanist/insets to get insets for jetpack compose and just add WindowCompat.setDecorFitsSystemWindows(window, false)
like @shogun-nassar points out correctly in 。
原回答:
提供最终答案:
不要在任何地方使用 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)
将应用栏保持在状态栏下方(注意 margin/padding 差异)。
根据https://developer.android.com/training/gestures/edge-to-edge#kotlin
只需在 BaseActivity 或您想要的任何 activity 上使用它:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
N.B: 不要在任何地方使用这个
android:fitsSystemWindows="true"
我正在尝试让边缘到边缘 (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>
这是我的 Activity:
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
的情况下它将自行处理插图),但我无法让它工作有工厂。
我发现 this article 关于将插图添加到视图的 padding
但因为 FAB 使用 margin
我不确定,如果我在正确的轨道上.
是否有关于如何处理边到边插入的文档、示例和最佳实践?似乎 AppBarLayout
等一些小部件可以优雅地处理它,但我怎样才能让 FAB 也调整它的边距?
更新 1
具体来说,当将 android:fitsSystemWindows=true
添加到 CoordinatorLayout
时,它也会处理插图,但有一个主要缺点:
我有两个布局,每个布局都有一个 CoordinatorLayout
: "parent layout" 定义了一个 CoordinatorLayout
和一个 AppBarLayout
和一个 FrameLayout
来保存实际内容"child layout" 被放置在后者中的 Fragment 使用。
因此,我无法将 android:fitsSystemWindows=true
添加到子布局,因为它会导致顶部(工具栏和内容之间)出现空白 space,而且我无法将其放入父布局,因为 FAB 不会更新到插图。
编辑 2022-01-16:
现在我们可以使用 https://google.github.io/accompanist/insets to get insets for jetpack compose and just add WindowCompat.setDecorFitsSystemWindows(window, false)
like @shogun-nassar points out correctly in
原回答:
提供最终答案:
不要在任何地方使用 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)
将应用栏保持在状态栏下方(注意 margin/padding 差异)。
根据https://developer.android.com/training/gestures/edge-to-edge#kotlin
只需在 BaseActivity 或您想要的任何 activity 上使用它:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
}
N.B: 不要在任何地方使用这个
android:fitsSystemWindows="true"