In this article I'll present how to handle activities with toolbars on Android 15 and later. I'll cover three scenarios: activity with system toolbar, activity with custom toolbar and compose layout.
Since Android 15, the previously optional possibility to enable Edge2Edge became mandatory, and now it's the default behavior of Android activities. However some people not have time or budget in their project to rewrite all screens to match this requirement. I'll present you three simple workarounds.
Add flag in activity's style
The simplest solution is adding following flag to the style of your activity:
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
This solution must be considered as a temporary one, because as Google recently announced, it won't work on Android 16 and later. I recommend using it only as a quick fix and work on other long term solution.
General mechanism for old-school activities with XML layouts
Google provided other way of handling edge to edge enforcement.
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
v.updateLayoutParams<MarginLayoutParams> {
leftMargin = insets.left
bottomMargin = insets.bottom
rightMargin = insets.right
}
WindowInsetsCompat.CONSUMED
}
Above code with description is available here.
This listener is designed to add margins for specific views (like floating action button) so they are not overlapped by system bars.
We can reuse it to handle margins for the entire activity.
Let's write some extension function:
fun Activity.addMarginsForEdgeToEdge() {
val view = this.window.decorView.findViewById<View>(android.R.id.content)
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
val insets = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars()
)
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.left
bottomMargin = insets.bottom
rightMargin = insets.right
topMargin = insets.top
}
WindowInsetsCompat.CONSUMED
}
}
We have used the same funcion, but as a view we have provided activity's decorView
.
For activities with system's default toolbar you can simply use this function in onCreate()
of your activity.
Handling custom toolbar
If your activity have some custom toolbar written specifically for your application, the code has to be a little bit more complicated. Since a toolbar is a part of your layout it is not considered as part of system bars
. If you simply add margins as presented above, the blank, semi-transparent space will be left above activity with clock, battery etc. It would definitely look better if this space remains the same color as your toolbar. To achieve this let's modify the solution:
fun Activity.addMarginsForEdgeToEdge(
getToolbar: () -> View?,
) {
var toolbarHeight: Int? = null
val view = this.window.decorView.findViewById<View>(android.R.id.content)
ViewCompat.setOnApplyWindowInsetsListener(view) { v, windowInsets ->
val insets = windowInsets.getInsets(
WindowInsetsCompat.Type.systemBars()
)
val toolbarView = getToolbar()
if(toolbarView != null) {
val layoutParams = toolbarView.layoutParams
if(toolbarHeight == null)
toolbarHeight = layoutParams.height
layoutParams.height = toolbarHeight?.plus(insets.top) ?: insets.top
toolbarView.layoutParams = layoutParams
toolbarView.updatePadding(
top = insets.top
)
}
else {
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top
}
}
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.left
bottomMargin = insets.bottom
rightMargin = insets.right
}
WindowInsetsCompat.CONSUMED
}
}
Our function now can get your custom toolbar's view as a parameter. If you won't provide it, the behavior won't change and the code will handle system toolbar.
If the toolbar provided is not-null, then we are not setting the top insets of activity. Instead we preserve the toolbar's height, and then we increase it by the top insets (which is now height of this semi transparent bar above our toolbar). Additionally we update top padding to make the toolbar's content centered as before.
Handling toolbars in compose
The workaround for compose is the simplest. We simply need to wrap our compose view in some function like that:
protected fun setContent(content: @Composable () -> Unit) {
Column(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars)
) {
content()
}
}
And that's all. Enjoy!
Top comments (0)