We will use the window inset listener so that each one record objects, together with the final record merchandise, are padded above the navigation bar.
// Determine 12
ViewCompat.setOnApplyWindowInsetsListener(
findViewById(R.id.recycler_view)
) { v, insets ->
val innerPadding = insets.getInsets(
// Discover we're utilizing systemBars, not statusBar
WindowInsetsCompat.Kind.systemBars()
// Discover we're additionally accounting for the show cutouts
or WindowInsetsCompat.Kind.displayCutout()
// If utilizing EditText, additionally add
// "or WindowInsetsCompat.Kind.ime()"
// to keep up focus when opening the IME
)
v.setPadding(
innerPadding.left,
innerPadding.prime,
innerPadding.proper,
innerPadding.backside)
insets
}
Nevertheless, now the app seems much less immersive. To get the end result we would like, add clipToPadding=false
to make sure the final record merchandise sits above the navigation bar and the record is seen whereas scrolling behind the navigation bar (and standing bar).
...
android:clipToPadding="false" />
5. Don’t neglect IMEs
Set android:windowSoftInputMode=”adjustResize”
in your Exercise’s AndroidManifest.xml entry to make room for the IME (or smooth keyboard) on display screen.
Dealing with IMEs insets in Compose
Account for the IME utilizing Modifier.imePadding()
. For instance, this may also help preserve deal with a TextField
in a LazyColumn
when the IME opens. See the Inset consumption part for a code instance and clarification.
Dealing with IMEs insets in Views
Earlier than concentrating on SDK 35, utilizing android:windowSoftInputMode=”adjustResize”
was all you wanted to keep up deal with — for instance — an EditText
in a RecyclerView
when opening an IME. With “adjustResize”
, the framework handled the IME because the system window, and the window’s root views had been padded so content material avoids the system window.
After concentrating on SDK 35, you have to additionally account for the IME utilizing ViewCompat.setOnApplyWindowInsetsListener
and WindowInsetsCompat.Kind.ime()
as a result of the framework will not pad the window’s root views. See Determine 12’s code instance.
6. For backward compatibility, use enableEdgeToEdge
as an alternative of setDecorFitsSystemWindows
After your app has dealt with insets, make your app edge-to-edge on earlier Android variations. For this, use enableEdgeToEdge
as an alternative of setDecorFitsSystemWindows
. The enableEdgeToEdge
methodology encapsulates the about 100 traces of code it’s good to be really backward suitable.
7. Background defend system bars solely when needed
In lots of circumstances, hold the brand new Android 15 defaults. The standing bar and gesture navigation bar ought to be clear, and three button navigation translucent after concentrating on SDK 35 (see Determine 1).
Nevertheless, there are some circumstances the place you want to protect the background coloration of the system bars, however the APIs to set the standing and navigation bar colours are deprecated. We’re planning to launch an AndroidX library to assist this use case. Within the meantime, in case your app should provide customized background safety to 3-button navigation or the standing bar, you’ll be able to place a composable or view behind the system bar utilizing WindowInsets.Kind#tappableElement()
to get the 3-button navigation bar peak or WindowInsets.Kind#statusBars
.
For instance, to indicate the colour of the aspect behind the 3-button navigation in Compose, set the window.isNavigationBarContrastEnforced
property to false. Setting this property to false makes 3-button navigation absolutely clear (be aware: this property does not have an effect on gesture navigation).
Then, use WindowInsets.tappableElement
to align UI behind insets for tappable system UI. If non-0, the person is utilizing tappable bars, like three button navigation. On this case, draw an opaque view or field behind the tappable bars.
class MainActivity : ComponentActivity() {
override enjoyable onCreate(savedInstanceState: Bundle?) {
tremendous.onCreate(savedInstanceState)
setContent {
window.isNavigationBarContrastEnforced = false
MyTheme {
Floor(...) {
MyContent(...)
ProtectNavigationBar()
}
}
}
}
}// Use provided that required.
@Composable
enjoyable ProtectNavigationBar(modifier: Modifier = Modifier) {
val density = LocalDensity.present
val tappableElement = WindowInsets.tappableElement
val bottomPixels = tappableElement.getBottom(density)
val usingTappableBars = bear in mind(bottomPixels) {
bottomPixels != 0
}
val barHeight = bear in mind(bottomPixels) {
tappableElement.asPaddingValues(density).calculateBottomPadding()
}
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Association.Backside
) {
if (usingTappableBars) {
Field(
modifier = Modifier
.background(MaterialTheme.colorScheme.background)
.fillMaxWidth()
.peak(barHeight)
)
}
}
}
The next ideas apply just for apps that use Jetpack Compose. See further Compose-related ideas on this video: Edge-to-edge and insets | Compose Ideas.
8. Use Scaffold’s PaddingValues
For Compose, use Scaffold
as an alternative of Floor
to arrange your app’s UI with TopAppBar
, BottomAppBar
, NavigationBar
, and NavigationRail
. Use Scaffold’s PaddingValues
parameter to inset your vital UI. Most often, that’s all it’s good to do.
Nevertheless, there are circumstances the place making use of Scaffold’s PaddingValues
will trigger surprising outcomes. Scaffold’s PaddingValues
consists of insets for the highest, backside, begin and finish edges of the display screen. Chances are you’ll want values for less than sure edges. One strategy is to make a replica of the parameter and manually modify prime, backside, begin and finish insets in order to not apply an excessive amount of padding.
PaddingValues
utilized to the enter discipline. The system makes use of the dimensions of the standing bar and the highest app bar to calculate the highest padding worth, which creates extra padding above the enter discipline. Proper: Scaffold’s PaddingValues utilized however with prime padding manually eliminated.Right here’s the inaccurate code, inflicting the surplus padding seen within the center picture of Determine 14.
// Causes extra padding, seen within the center picture of Determine 14.
Scaffold { innerPadding -> // innerPadding is Scaffold's PaddingValues
InputBar(
...
contentPadding = innerPadding
) {...}
}
Right here’s the corrected code that generates correct padding, as seen within the right-side picture of Determine 14.
// Operate to make a replica of PaddingValues, utilizing present defaults until an
// various worth is specified
personal enjoyable PaddingValues.copy(
layoutDirection: LayoutDirection,
begin: Dp? = null,
prime: Dp? = null,
finish: Dp? = null,
backside: Dp? = null,
) = PaddingValues(
begin = begin ?: calculateStartPadding(layoutDirection),
prime = prime ?: calculateTopPadding(),
finish = finish ?: calculateEndPadding(layoutDirection),
backside = backside ?: calculateBottomPadding(),
)// Produces appropriate padding, seen within the right-side picture of Determine 14.
Scaffold { innerPadding -> // innerPadding is Scaffold's PaddingValues
val layoutDirection = LocalLayoutDirection.present
InputBar(
...
contentPadding = innerPadding.copy(layoutDirection, prime = 0.dp)
) {...}
}
9. Use excessive degree WindowInsets APIs
Much like Scaffold’s PaddingValues
, you can even use the high-level WindowInset
APIs to simply and safely draw vital UI parts. These are:
See Inset fundamentals to be taught extra.
The next apply just for Views-based apps.
10. Favor ViewCompat.setOnApplyWindowInsetsListener
over fitsSystemWindows=true
You may use fitsSystemWindows=true
to inset your app’s content material. It’s a straightforward 1-line code change. Nevertheless, don’t use fitsSystemWindows
on a View that incorporates your total format (together with the background). It will make your app look not edge-to-edge as a result of fitsSystemWindows
handles insets on all edges.
fitsSystemWindows
can create an edge-to-edge expertise if utilizing CoordinatorLayouts
or AppBarLayouts.
Add fitsSystemWindows
to the CoordinatorLayout
and the AppBarLayout
, and the AppBarLayout
attracts edge-to-edge, which is what we would like.
android:fitsSystemWindows="true"
...>
android:fitsSystemWindows="true"
...>
android:textual content="App Bar Structure"
.../>
On this case, AppBarLayout
used fitsSystemWindows
to attract beneath the standing bar slightly than avoiding it, which is the other of what we would anticipate. Moreover, AppBarLayout
with fitsSystemWindows=true
solely applies padding for the highest and never the underside, begin, or finish edges.
The CoordinatorLayout
and AppBarLayout
objects have the next habits when overriding fitsSystemWindows
:
CoordinatorLayout
: backgrounds of kid views draw beneath the system bars if these views additionally setfitsSystemWindows=true
. Padding is routinely utilized to the content material of these Views (e.g. textual content, icons, photos) to account for system bars and show cutouts.AppBarLayout
: attracts beneath the system bars iffitsSystemWindows=true
and routinely applies prime padding to content material.
Most often, deal with insets with ViewCompat.setOnApplyWindowInsetsListener
as a result of it lets you outline which edges ought to deal with insets and has constant habits. See ideas #4 and #11 for a code instance.
11. Apply insets primarily based on app bar peak through the format section
In the event you discover that your app’s content material is hiding beneath an app bar, you may want to use insets after the app bar is laid out, taking the app bar peak under consideration.
For instance, when you’ve got scrolling content material beneath an AppBarLayout
in a FrameLayout
, you possibly can use code like this to make sure the scrolling content material seems after the AppBarLayout
. Discover padding is utilized inside doOnLayout
.
val myScrollView = findViewById(R.id.my_scroll_view)
val myAppBar = findViewById(R.id.my_app_bar_layout)ViewCompat.setOnApplyWindowInsetsListener(myScrollView) { scrollView, windowInsets ->
val insets = windowInsets.getInsets(
WindowInsetsCompat.Kind.systemBars() or WindowInsetsCompat.Kind.displayCutout()
)
myAppBar.doOnLayout { appBar ->
scrollView.updatePadding(
left = insets.left,
proper = insets.proper,
prime = appBar.peak,
backside = insets.backside
)
}
WindowInsetsCompat.CONSUMED
}
Likewise, when you’ve got scrolling content material that ought to sit above a BottomNavigationView
, you’ll wish to account for the BottomNavigationView’s peak as soon as it’s laid out.
It would take vital work to correctly assist an edge-to-edge expertise. Earlier than you goal SDK 35, contemplate how lengthy it’s good to make the mandatory adjustments in your app.
In the event you want extra time to deal with insets to be suitable with the system’s default edge-to-edge habits, you’ll be able to briefly opt-out utilizing R.attr#windowOptOutEdgeToEdgeEnforcement
. However do not plan to make use of this flag indefinitely as it is going to be non-functional within the close to future.
The flag may be notably useful for apps which have tens to lots of of Actions. You may opt-out every Exercise, then — make your app edge-to-edge one Exercise at a time.
Right here’s one strategy to utilizing this flag. Assuming your minSDK is lower than 35, this attribute have to be in values-v35.xml
.
Create an empty fashion for previous variations in values.xml
:
Name the fashion earlier than accessing the decor view in setContentView
:
class MainActivity : AppCompatActivity() {
override enjoyable onCreate(savedInstanceState: Bundle?) {// Name earlier than the DecorView is accessed in setContentView
theme.applyStyle(R.fashion.OptOutEdgeToEdgeEnforcement, /* power */ false)
tremendous.onCreate(savedInstanceState)
setContentView(R.format.activity_main)
...
}
}
Android 15 AOSP launched at the moment. Our workforce has created blogs, movies, and codelabs to assist get your app able to deal with the Android 15 edge-to-edge enforcement. What follows is an inventory of previous and new assets for additional studying.