Voraussetzungen
- Das Projekt MUSS Android Jetpack Compose verwenden.
- Das Projekt MUSS auf SDK 35 oder höher abzielen. Falls das SDK niedriger als 35 ist, erhöhe das SDK auf 35.
Schritt 1: Planen
- Lokalisiere und analysiere alle Activity-Klassen, um festzustellen, welche bereits Edge-to-Edge-Unterstützung haben. Plane für jede Activity ohne Edge-to-Edge, diese nachzurüsten.
- Lokalisiere und analysiere in jeder Activity alle Listen und FAB-Komponenten, um zu erkennen, welche bereits Edge-to-Edge-Unterstützung haben. Für jede Komponente ohne Edge-to-Edge-Unterstützung plane, diese nachzurüsten.
- Scanne in jeder Activity nach
TextField,OutlinedTextFieldoderBasicTextField. Falls gefunden, MUSST du überprüfen, dass die IME das Eingabefeld nicht verdeckt, indem du dem IME-Abschnitt dieser Fähigkeit folgst.
Schritt 2: Edge-to-Edge-Unterstützung hinzufügen
- Füge
enableEdgeToEdgevorsetContentinonCreatein jeder Activity hinzu, die nicht bereitsenableEdgeToEdgeaufruft. - Füge
android:windowSoftInputMode="adjustResize"in der AndroidManifest.xml für alle Activities hinzu, die eine Soft-Tastatur verwenden.
Schritt 3: Insets anwenden
-
Die App MUSS System-Insets anwenden oder Inhalte an Lineale ausrichten, damit wichtige UI-Elemente berührbar bleiben. Wähle nur eine Methode, um doppelte Abstände zu vermeiden:
- BEVORZUGT: Wenn verfügbar, verwende
Scaffolds und übergibPaddingValuesan das Content-Lambda.
Scaffold { innerPadding -> // innerPadding berücksichtigt Systemleisten und alle Scaffold-Komponenten LazyColumn( modifier = Modifier .fillMaxSize() .consumeWindowInsets(innerPadding), contentPadding = innerPadding ) { /* Inhalt */ } }-
BEVORZUGT: Wenn verfügbar, verwende die automatische Inset-Behandlung oder Padding-Modifikatoren in Material-Komponenten.
- Material 3 Komponenten verwalten sichere Bereiche für ihre eigenen Komponenten, einschließlich:
TopAppBarSmallTopAppBarCenterAlignedTopAppBarMediumTopAppBarLargeTopAppBarBottomAppBarModalDrawerSheetDismissibleDrawerSheetPermanentDrawerSheetModalBottomSheetNavigationBarNavigationRail
- Für Material 2 Komponenten verwende den
windowInsets-Parameter, um Insets manuell fürBottomAppBar,TopAppBarundBottomNavigationanzuwenden. WENDE KEIN Padding auf den übergeordneten Container an; übergib stattdessen Insets direkt an die App-Bar-Komponente. Das Anwenden von Padding auf den übergeordneten Container verhindert, dass der App-Bar-Hintergrund in den Systemleistenbereich gezeichnet wird. Wähle fürTopAppBarbeispielsweise nur eine der folgenden Optionen:- BEVORZUGT:
TopAppBar(windowInsets = AppBarDefaults.topAppBarWindowInsets) TopAppBar(windowInsets = WindowInsets.systemBars.exclude(WindowInsets.navigationBars))TopAppBar(windowInsets = WindowInsets.systemBars.add(WindowInsets.captionBar))
- BEVORZUGT:
- Material 3 Komponenten verwalten sichere Bereiche für ihre eigenen Komponenten, einschließlich:
-
Für Komponenten außerhalb eines Scaffolds verwende Padding-Modifikatoren wie
Modifier.safeDrawingPadding()oderModifier.windowInsetsPadding(WindowInsets.safeDrawing).Box( modifier = Modifier .fillMaxSize() .safeDrawingPadding() ) { Button( onClick = {}, modifier = Modifier.align(Alignment.BottomCenter) ) { Text("Login") } } -
Für tief verschachtelte Komponenten mit übermäßigem Padding verwende
WindowInsetsRulers(z. B.Modifier.fitInside(WindowInsetsRulers.SafeDrawing.current)). Siehe den Abschnitt IME für ein Codebeispiel. -
Wenn ein Element (z. B. ein benutzerdefinierter Header oder ein dekorativer Scrim) die Abmessungen einer Systemleiste haben soll, verwende Inset-Größen-Modifikatoren (z. B.
Modifier.windowInsetsTopHeight(WindowInsets.systemBars)). Siehe den Abschnitt Listen für ein Codebeispiel.
- BEVORZUGT: Wenn verfügbar, verwende
Adaptive Scaffolds
NavigationSuiteScaffoldverwaltet sichere Bereiche für eigene Komponenten wie dieNavigationRailoderNavigationBar. Adaptive Scaffolds (z. B.NavigationSuiteScaffold,ListDetailPaneScaffold) gebenPaddingValuesjedoch nicht an ihre inneren Inhalte weiter. Du MUSST Insets auf einzelne Bildschirme oder Komponenten anwenden (z. B. Listen-contentPaddingoder FAB-Padding), wie in Schritt 3 beschrieben. WENDE KEINsafeDrawingPaddingoder ähnliche Modifikatoren auf das übergeordneteNavigationSuiteScaffoldan. Dieses Clipping verhindert einen randlosen Bildschirm.
IME
- Überprüfe für jede Activity mit einer Soft-Tastatur, ob
android:windowSoftInputMode="adjustResize"in der AndroidManifest.xml gesetzt ist. Verwende NICHTSOFT_INPUT_ADJUST_RESIZE, da es veraltet ist. Erhalte dann den Fokus auf das Eingabefeld. Wähle eine Option:-
- BEVORZUGT: Füge
Modifier.fitInside(WindowInsetsRulers.Ime.current)zum Inhaltscontainer hinzu. Dies wirdimePadding()vorgezogen, da es Ruckler und zusätzliches Padding reduziert, das durch das Vergessen, Insets weiter oben in der Hierarchie zu konsumieren, verursacht wird.
- BEVORZUGT: Füge
-
- Füge
imePaddingzum Inhaltscontainer hinzu. Der Padding-Modifikator MUSS vorModifier.verticalScroll()platziert werden. Verwende NICHTModifier.imePadding(), wenn das übergeordnete Element bereits die IME mitcontentWindowInsetsberücksichtigt (z. B.contentWindowInsets = WindowInsets.safeDrawing). Dies würde zu doppeltem Padding führen.
- Füge
-
IMEs mit Scaffolds Codebeispiele
RICHTIG
RICHTIG, weil contentWindowInsets IME-Insets enthält, die als innerPadding an das Content-Lambda übergeben werden.
// RICHTIG
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
.verticalScroll(rememberScrollState())
) { /* Inhalt */ }
}
RICHTIG, weil fitInside den Inhalt unabhängig von contentWindowInsets an die IME-Insets anpasst.
// RICHTIG
Scaffold() { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
.fitInside(WindowInsetsRulers.Ime.current)
.verticalScroll(rememberScrollState())
) { /* Inhalt */ }
}
RICHTIG, weil das Standard-contentWindowInsets keine IME-Insets enthält und imePadding() IME-Insets anwendet:
// RICHTIG
Scaffold() { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.consumeWindowInsets(innerPadding)
.imePadding()
.verticalScroll(rememberScrollState())
) { /* Inhalt */ }
}
FALSCH
FALSCH, weil es überschüssiges Padding gibt, wenn die IME geöffnet wird. IME-Insets werden zweimal angewendet, einmal mit innerPadding, das IME-Insets aus den übergebenen contentWindowInsets-Werten enthält, und einmal mit imePadding:
// FALSCH
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.imePadding()
.verticalScroll(rememberScrollState())
) { /* Inhalt */ }
}
FALSCH, weil die IME den Inhalt verdecken wird. Das Standard-contentWindowInsets des Scaffolds enthält KEINE IME-Insets.
// FALSCH
Scaffold() { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
) { /* Inhalt */ }
}
IMEs ohne Scaffolds Codebeispiele
RICHTIG
Die folgenden Codebeispiele werden KEIN übermäßiges Padding verursachen.
// RICHTIG
Box(
// Insets verbraucht
modifier = Modifier.safeDrawingPadding() // oder imePadding(), safeContentPadding(), safeGesturesPadding()
) {
Column(
modifier = Modifier.imePadding()
) { /* Inhalt */ }
}
// RICHTIG
Box(
// Insets verbraucht
modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing) // oder WindowInsets.ime, WindowInsets.safeContent, WindowInsets.safeGestures
) {
Column(
modifier = Modifier.imePadding()
) { /* Inhalt */ }
}
// RICHTIG
Box(
// Insets nicht verbraucht, aber aufgrund von fitInside irrelevant
modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()) // oder WindowInsets.ime.asPaddingValues(), WindowInsets.safeContent.asPaddingValues(), WindowInsets.safeGestures.asPaddingValues()
) {
Column(
modifier = Modifier
.fillMaxSize()
.fitInside(WindowInsetsRulers.Ime.current)
) { /* Inhalt */ }
}
FALSCH
Das folgende Codebeispiel wird übermäßiges Padding verursachen, weil IME-Insets zweimal angewendet werden:
// FALSCH
Box(
// Insets nicht verbraucht
modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()) // oder WindowInsets.ime.asPaddingValues(), WindowInsets.safeContent.asPaddingValues(), WindowInsets.safeGestures.asPaddingValues()
) {
Column(
modifier = Modifier.imePadding()
) { /* Inhalt */ }
}
Navigationsleisten-Kontrast und Systemleisten-Symbole
-
Wenn die Activity
enableEdgeToEdgeausWindowCompatverwendet, MUSST duisAppearanceLightNavigationBarsundisAppearanceLightStatusBarsauf die Umkehrung des Gerätethemas setzen, damit die Systemleistensymbole lesbar sind. Es wird empfohlen, dies in deiner Theme-Datei zu tun. TU dies NICHT, wenn die ActivitiesenableEdgeToEdgeausComponentActivityverwenden, da dies die Icon-Farben automatisch behandelt.// Nur verwenden, wenn `enableEdgeToEdge` aus `WindowCompat` aufgerufen wird. // In deine Theme-Datei einfügen. @Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val view = LocalView.current if (!view.isInEditMode) { SideEffect { val window = (view.context as? Activity)?.window ?: return@SideEffect val controller = WindowCompat.getInsetsController(window, view) // Dunkle Symbole für hellen Modus (!darkTheme), helle Symbole für dunklen Modus controller.isAppearanceLightStatusBars = !darkTheme controller.isAppearanceLightNavigationBars = !darkTheme } } MaterialTheme(content = content) } -
Wenn ein Bildschirm ein
Scaffoldoder einNavigationSuiteScaffoldmit einer unteren Leiste (z. B.BottomAppBar,NavigationBar) verwendet, setzewindow.isNavigationBarContrastEnforced = falsein der entsprechenden Activity für SDK 29+. Dies verhindert, dass das System einen durchscheinenden Hintergrund zur Navigationsleiste hinzufügt, und stellt sicher, dass die Farben deiner unteren Leiste bis zum unteren Bildschirmrand reichen.
Listen
- Wende Inset-Padding (wie
Scaffold'sinnerPadding) auf dencontentPadding-Parameter von scrollbaren Komponenten an (z. B.LazyColumn,LazyRow). Wende es NICHT alsModifier.padding()auf den übergeordneten Container der Liste an, da dies den Inhalt beschneidet und das Scrollen hinter die Systemleisten verhindert. - Erstelle eine durchscheinende Composable, die die Systemleiste abdeckt, damit die Symbole weiterhin lesbar sind.
class SystemBarProtectionSnippets : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// enableEdgeToEdge setzt window.isNavigationBarContrastEnforced = true
// was verwendet wird, um einen durchscheinenden Scrim zur Drei-Tasten-Navigation hinzuzufügen
enableEdgeToEdge()
setContent {
MyTheme {
// Hauptinhalt
MyContent()
// Nach dem Zeichnen des Hauptinhalts, Statusleisten-Schutz zeichnen
StatusBarProtection()
}
}
}
}
@Composable
private fun StatusBarProtection(
color: Color = MaterialTheme.colorScheme.surfaceContainer,
) {
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(
with(LocalDensity.current) {
(WindowInsets.statusBars.getTop(this) * 1.2f).toDp()
}
)
.background(
brush = Brush.verticalGradient(
colors = listOf(
color.copy(alpha = 1f),
color.copy(alpha = 0.8f),
Color.Transparent
)
)
)
)
}
Dialoge
Wenn beide folgenden Bedingungen zutreffen, ist der Dialog Vollbild und muss randlos gemacht werden:
- Die
DialogPropertiesenthaltenusePlatformDefaultWidth = false. - Der Dialog ruft
Modifier.fillMaxSize()auf.
Um einen Vollbild-Dialog randlos zu machen, setze decorFitsSystemWindows = false in den DialogProperties.
Dialog(
onDismissRequest = { /* Schließen behandeln */ },
properties = DialogProperties(
// 1. Ermöglicht dem Dialog, die gesamte Breite des Bildschirms zu nutzen
usePlatformDefaultWidth = false,
// 2. Ermöglicht dem Dialog, hinter Status- und Navigationsleisten zu zeichnen
decorFitsSystemWindows = false
)
) { /* Inhalt */ }
Checkliste
- [ ] Ruft jede
ActivityenableEdgeToEdge()auf? - [ ] Ist
adjustResizein derAndroidManifest.xmlgesetzt? - [ ] Hat jedes
TextField,OutlinedTextFieldoderBasicTextFieldein übergeordnetes Element mitimePadding(),fitInside,Modifier.safeDrawingPadding(),Modifier.safeContentPadding(),Modifier.safeGesturesPadding()odercontentWindowInsetsgesetzt aufWindowInsets.safeDrawingoderWindowInsets.ime? - [] Zeichnet das erste und letzte Listenelement mit Abstand zu den Systemleisten, indem Insets an
contentPaddingübergeben werden? - [ ] Zeichnen FABs über den Navigationsleisten, entweder innerhalb eines Scaffolds oder durch Anwenden von
Modifier.safeDrawingPadding()? - [ ] Baut das Projekt? Führe
./gradlew buildaus, um sicherzugehen.