简体   繁体   English

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

[英]Go edge-to-edge on Android correctly with WindowInsets

I'm trying to get edge-to-edge ( https://youtu.be/OCHEjeLC_UY?t=1635 ) working correctly on API 21 up to 29.我正在尝试让边缘到边缘( https://youtu.be/OCHEjeLC_UY?t=1635 )在 API 21 到 29 上正常工作。

I'm using this on my v27\\themes.xml :我在我的v27\\themes.xml上使用它:

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

And this in my 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
    }
}

Plus, I'm setting android:fitsSystemWindows=true on my AppBarLayout .另外,我在AppBarLayout上设置了android:fitsSystemWindows=true


With that in place, it looks fine on API >= 27 where the content scrolls behind the now transparent navigation bar but on older APIs, the content is covered by the black navigation bar.有了它,它在 API >= 27 上看起来很好,其中内容在现在透明的导航栏后面滚动,但在较旧的 API 上,内容被黑色导航栏覆盖。

I know that I need to get the WindowInsets and add it to my existing padding (or in case of AppBarLayout it will handle the insets itself), but I cannot get it to work with a FAB.我知道我需要获取WindowInsets并将其添加到我现有的填充中(或者在AppBarLayout的情况下,它将自己处理插图),但我无法让它与 FAB 一起使用。

I found this article about adding the inset to the padding of a view but because the FAB uses margin I'm not sure, if I'm on the right track.我发现这篇文章是关于将插图添加到视图的padding中,但由于 FAB 使用margin ,我不确定我是否走在正确的轨道上。


Is there any documentation, example, best practice on how to handle insets when going edge-to-edge?是否有任何文档,例如,关于如何在边缘到边缘处理插入的最佳实践? It seems that some widgets like AppBarLayout handle it gracefully but how can I get the FAB to adjust its margin as well?似乎AppBarLayout之类的一些小部件可以优雅地处理它,但我怎样才能让 FAB 也调整其边距?


Update 1更新 1

To specify, when adding android:fitsSystemWindows=true to CoordinatorLayout it handles the insets as well but with a major drawback:要指定,当将android:fitsSystemWindows=true添加到CoordinatorLayout时,它也会处理插入,但有一个主要缺点:

I have two layouts, each with a CoordinatorLayout : "parent layout" defines a CoordinatorLayout with a AppBarLayout and a FrameLayout to hold the actual content and "child layout" used by a Fragment placed in the latter.我有两个布局,每个布局都有一个CoordinatorLayout :“父布局”定义了一个带有AppBarLayoutCoordinatorLayout和一个FrameLayout来保存放置在后者中的 Fragment 使用的实际内容和“子布局”。

Because of this, I cannot add android:fitsSystemWindows=true to the child layout because it would result in a blank space on top (between the toolbar and the content) and I cannot put it in the parent layout because then the FAB won't be updated to the insets.因此,我无法将android:fitsSystemWindows=true添加到子布局中,因为它会导致顶部(工具栏和内容之间)有一个空白区域,并且我不能将它放在父布局中,因为这样 FAB 不会更新到插图。

To provide a final answer: 提供最终答案:

Do not use android:fitsSystemWindows anywhere but apply insets manually to any view at the edge of the screen which would otherwise slip behind the system bars (eg AppBarLayout or FloatingActionButton ). 不要在任何地方使用android:fitsSystemWindows ,而android:fitsSystemWindows插图手动应用于屏幕边缘的任何视图,否则这些视图会滑入系统栏后面(例如AppBarLayoutFloatingActionButton )。

I wrote some helpers to add the insets to either padding or margin, respecting any previously added one (needs androidx.core:core:1.2.0-alpha01 ): 我编写了一些帮助程序,以将插图添加到padding或margin中,并尊重任何先前添加的插图(需要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
    }
}

As an example, simply call fab.addSystemWindowInsetToMargin(bottom = true) and the FAB will be moved up above the navigation bar. 例如,只需调用fab.addSystemWindowInsetToMargin(bottom = true) ,FAB就会在导航栏上方上移。 Or app_bar.addSystemWindowInsetToPadding(top = true) to keep the app bar below the status bar (mind the margin/padding difference). app_bar.addSystemWindowInsetToPadding(top = true)将应用程序栏保持在状态栏下方(注意边距/填充差异)。

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

Just use this on BaseActivity or any activity you want:只需在 BaseActivity 或您想要的任何活动上使用它:

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

NB: Do not use anywhere this注意:不要在任何地方使用这个

android:fitsSystemWindows="true"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM