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?
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.
Edit 2022-01-16:
Nowadays we can use 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 his answer.
Original answer:
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 (e.g. AppBarLayout
or FloatingActionButton
).
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):
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. Or app_bar.addSystemWindowInsetToPadding(top = true)
to keep the app bar below the status bar (mind the margin/padding difference).