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.

I'm using this on my 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.


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.

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.

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.


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?


Update 1

To specify, when adding android:fitsSystemWindows=true to CoordinatorLayout it handles the insets as well but with a major drawback:

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.

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.

2 ответа

Решение

Чтобы дать окончательный ответ:

Не использовать 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) чтобы панель приложения оставалась ниже строки состояния (обратите внимание на разницу полей / отступов).

Согласно https://developer.android.com/training/gestures/edge-to-edge#kotlin

Просто используйте это в BaseActivity или любой активности, которую вы хотите:

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

NB: нигде не используйте это

      android:fitsSystemWindows="true"
Другие вопросы по тегам