Inhalte in der App von Rand zu Rand anzeigen lassen und Fenstereinblendungen in „Compose“ bearbeiten

Die Android-Plattform ist für das Zeichnen der System-UI wie der Statusleiste und der Navigationsleiste verantwortlich. Diese System-UI wird unabhängig davon angezeigt, welche App der Nutzer verwendet.

WindowInsets stellt Informationen zum System bereit. Benutzeroberfläche, um sicherzustellen, dass Ihre App im richtigen Bereich gezeichnet wird und nicht verdeckt wird über die System-UI.

Vollbildmodus verwenden, um hinter den Systemleisten zu zeichnen
Abbildung 1: Sie gehen von Rand zu Rand, um hinter den Systemleisten zu zeichnen.

Unter Android 14 (API-Level 34) und niedriger wird die UI Ihrer App nicht darunter dargestellt. Systemleisten und Display-Aussparungen standardmäßig.

Unter Android 15 (API-Level 35) und höher wird Ihre App unter dem System angezeigt Balken und Display-Aussparungen, sobald Ihre App auf SDK 35 ausgerichtet ist. Das führt zu einer besseren Nutzererfahrung und ermöglicht es Ihrer App, den gesamten verfügbaren Fensterbereich zu nutzen.

Wenn Inhalte hinter der System-UI angezeigt werden, wird dies als Bildschirmrand-zu-Bildschirmrand-Ansicht bezeichnet. Auf dieser Seite erfahren Sie mehr über die verschiedenen Arten von Einblendungen, wie Sie den gesamten Bildschirm nutzen und wie Sie die Einblendungs-APIs verwenden, um Ihre Benutzeroberfläche zu animieren und dafür zu sorgen, dass die Inhalte Ihrer App nicht von System-UI-Elementen verdeckt werden.

Eingefügte Grundlagen

Wenn eine App raffiniert wird, müssen Sie dafür sorgen, dass wichtige Inhalte und Interaktionen werden von der System-UI nicht verdeckt. Wenn eine Schaltfläche beispielsweise hinter der Navigationsleiste platziert ist, kann der Nutzer möglicherweise nicht darauf klicken.

Die Größe der System-Benutzeroberfläche und Informationen zum Speicherort werden angegeben. über insets.

Jeder Teil der System-UI hat einen entsprechenden Einsatztyp, der beschreibt, und wo es platziert wird. Einblendungen für die Statusleiste geben beispielsweise die Größe und Position der Statusleiste an, während Einblendungen für die Navigationsleiste die Größe und Position der Navigationsleiste angeben. Jede Art von Einfügung besteht aus vier Pixelabmessungen: oben, links, rechts und unten. Diese Abmessungen geben an, wie weit sich die System-UI von den entsprechenden Seiten des App-Fensters erstreckt. Um Überschneidungen mit dieser Art von System-UI zu vermeiden, muss die App-Benutzeroberfläche um diesen Betrag eingerückt sein.

Diese integrierten Android-Einfügungstypen sind über WindowInsets verfügbar:

WindowInsets.statusBars

Die Einfügungen zur Beschreibung der Statusleisten. Das sind die oberen System-UI-Leisten mit Benachrichtigungssymbolen und anderen Indikatoren.

WindowInsets.statusBarsIgnoringVisibility

Die Statusleiste wird eingerückt, wenn sie sichtbar ist. Wenn die Statusleisten derzeit ausgeblendet sind (weil der immersive Vollbildmodus aktiviert ist), sind die Einzüge der Hauptstatusleiste leer.

WindowInsets.navigationBars

Die Einfügungen zur Beschreibung der Navigationsleisten. Dies sind die System-UI-Leisten auf der linken, rechten oder unteren Seite des Geräts, die die Taskleiste oder Navigationssymbole beschreiben. Diese können sich zur Laufzeit ändern, je nachdem, welche Navigationsmethode der Nutzer bevorzugt und wie er mit der Taskleiste interagiert.

WindowInsets.navigationBarsIgnoringVisibility

Die Navigationsleiste wird eingerückt, wenn sie sichtbar ist. Wenn die Navigationsleisten derzeit ausgeblendet sind (aufgrund des immersiven Vollbildmodus), sind die Einfügungen der Hauptnavigationsleiste leer, aber diese Einfügungen sind nicht leer.

WindowInsets.captionBar

Der Einschub, der die Fensterdekoration der System-UI beschreibt, wenn sich das Fenster in einem freiformigen Fenster befindet, z. B. die obere Titelleiste.

WindowInsets.captionBarIgnoringVisibility

Die Untertitelleiste wird eingeblendet, wenn sie sichtbar ist. Wenn die Untertitelleisten derzeit ausgeblendet sind, sind die Einfügungen der Hauptuntertitelleiste leer, aber diese Einfügungen sind nicht leer.

WindowInsets.systemBars

Die Gesamtheit der Systemleisten-Einfügungen, einschließlich der Statusleisten, Navigationsleisten und der Untertitelleiste.

WindowInsets.systemBarsIgnoringVisibility

Die Systemleisteneinfügungen für den Zeitpunkt, zu dem sie sichtbar sind. Wenn die Systemleisten derzeit ausgeblendet sind (aufgrund des immersiven Vollbildmodus), sind die Haupteinfügungen der Systemleiste leer. Diese Einfügungen sind jedoch nicht leer.

WindowInsets.ime

Die Einsätze, die angeben, wie viel Platz die Softwaretastatur unten einnimmt.

WindowInsets.imeAnimationSource

Die Einblendungen, die den Platz beschreiben, den die Softwaretastatur vor der aktuellen Tastaturanimation belegt hat.

WindowInsets.imeAnimationTarget

Die Einzüge, die den Platz beschreiben, den die Softwaretastatur nach der aktuellen Tastaturanimation einnimmt.

WindowInsets.tappableElement

Eine Art von Einblendungen, die detailliertere Informationen zur Navigations-UI enthalten und den Bereich angeben, in dem „Tippen“ vom System und nicht von der App verarbeitet wird. Bei transparenten Navigationsleisten mit Gestennavigation können einige App-Elemente über die Systemnavigations-UI angetippt werden.

WindowInsets.tappableElementIgnoringVisibility

Die antippbaren Elementeinsätze, die sichtbar sind. Wenn die antippbaren Elemente derzeit ausgeblendet sind (aufgrund des immersiven Vollbildmodus), sind die Haupteinfügungen der antippbaren Elemente leer. Diese Einfügungen sind jedoch nicht leer.

WindowInsets.systemGestures

Die Einfügungen, die die Anzahl der Einfügungen darstellen, bei denen das System Gesten zur Navigation abfängt. Apps können die Verarbeitung einer begrenzten Anzahl dieser Touch-Gesten über Modifier.systemGestureExclusion manuell festlegen.

WindowInsets.mandatorySystemGestures

Ein Teil der Systemgesten, die immer vom System verarbeitet werden und die nicht über Modifier.systemGestureExclusion deaktiviert werden können.

WindowInsets.displayCutout

Die Einsätze, die den Abstand darstellen, der erforderlich ist, um eine Überlappung mit einer Display-Aussparung (Kerbe oder Nadelloch) zu vermeiden.

WindowInsets.waterfall

Die Einsätze, die die gekrümmten Bereiche einer Wasserfalldarstellung darstellen. Ein Waterfall-Display hat an den Rändern des Displays gekrümmte Bereiche, in denen das Display an den Seiten des Geräts umgeschlagen wird.

Diese Typen werden in drei „sichere“ Einblendungstypen zusammengefasst, die dafür sorgen, dass Inhalte nicht verdeckt werden:

Diese „sicheren“ Einbettungstypen schützen Inhalte je nach zugrunde liegender Plattform auf unterschiedliche Weise:

  • Verwenden Sie WindowInsets.safeDrawing, um Inhalte zu schützen, die nicht unter der Benutzeroberfläche des Systems gezeichnet werden sollen. Dies ist die häufigste Verwendung von Einblendungen: Sie verhindern, dass Inhalte gezeichnet werden, die teilweise oder vollständig von der Benutzeroberfläche des Systems verdeckt werden.
  • Verwenden Sie WindowInsets.safeGestures, um Inhalte mit Touch-Gesten zu schützen. Dieses vermeidet, dass System-Touch-Gesten mit App-Gesten in Konflikt stehen (z. B. für die unteren Tabellenblätter, Karussells oder in Spielen).
  • Verwenden Sie WindowInsets.safeContent als Kombination aus WindowInsets.safeDrawing und WindowInsets.safeGestures, damit sich Inhalte nicht überschneiden und es keine Überschneidungen bei Gesten gibt.

Einrichtung von Einsätzen

Um Ihrer App die volle Kontrolle darüber zu geben, wo sie Inhalte abruft, führen Sie die folgenden Einstellungen aus Schritte. Ohne diese Schritte zeichnet Ihre App möglicherweise schwarze oder leuchtende Farben hinter dem System-UI oder nicht synchron mit der Softwaretastatur animiert werden.

  1. Wählen Sie SDK 35 oder höher aus, um unter Android 15 und höher Edge-to-Edge zu erzwingen. Ihre App hinter der System-UI angezeigt. Sie können die Benutzeroberfläche Ihrer App anpassen, indem Sie Einsätze.
  2. Optional können Sie enableEdgeToEdge() in Activity.onCreate() aufrufen, damit Ihre App in älteren Android-Versionen randlos angezeigt wird.
  3. Du kannst android:windowSoftInputMode="adjustResize" in deinen Aktivitäten festlegen: AndroidManifest.xml-Eintrag. Mit dieser Einstellung kann Ihre App die Größe der Software-IME als Einzüge erhalten. So können Sie Inhalte entsprechend auffüllen und layouten, wenn die IME in Ihrer App erscheint und verschwindet.

    <!-- in your AndroidManifest.xml file: -->
    <activity
      android:name=".ui.MainActivity"
      android:label="@string/app_name"
      android:windowSoftInputMode="adjustResize"
      android:theme="@style/Theme.MyApplication"
      android:exported="true">
    

Compose-APIs

Sobald Ihre Aktivität die Verarbeitung aller Einblendungen übernommen hat, können Sie mit Compose APIs dafür sorgen, dass Inhalte nicht verdeckt und interaktive Elemente nicht mit der System-UI überlappen. Diese APIs synchronisieren auch das Layout Ihrer App mit den Änderungen am Insert.

So wenden Sie die Einblendungen beispielsweise auf den Inhalt Ihrer gesamten App an:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

Dieses Snippet wendet die safeDrawing-Fenstereinsätze als Abstand um den den gesamten Inhalt der App. So wird sichergestellt, dass interaktive Elemente sich mit der System-UI überschneidet, bedeutet dies auch, dass keine der Apps hinter der Benutzeroberfläche des Systems, um einen Edge-to-Edge-Effekt zu erzielen. Damit Sie das gesamte Fenster optimal nutzen können, müssen Sie die Platzierung der Einblendungen auf Bildschirm- oder Komponentenebene optimieren.

Alle diese Einblendungstypen werden automatisch mit IME-Animationen animiert, die auf API 21 zurückportiert wurden. Alle Layouts, in denen diese Einfügungen verwendet werden, automatisch animiert, wenn sich die eingefügten Werte ändern.

Es gibt zwei Hauptmethoden, mit denen Sie diese Arten von Einzügen verwenden können, um Ihre zusammensetzbaren Layouts anzupassen: Padding-Modifikatoren und Modifikatoren für die Einzuggröße.

Modifikatoren für das Padding

Modifier.windowInsetsPadding(windowInsets: WindowInsets) wendet die vorgegebenen Fenstereinfügungen als Abstand und verhält sich dann genauso wie Modifier.padding. Bei Modifier.windowInsetsPadding(WindowInsets.safeDrawing) werden die sicheren Zeichenbereiche beispielsweise als Abstand auf allen vier Seiten angewendet.

Außerdem gibt es mehrere integrierte Dienstprogrammmethoden für die gängigsten Arten von Einträgen. Modifier.safeDrawingPadding() ist eine solche Methode, entspricht Modifier.windowInsetsPadding(WindowInsets.safeDrawing). Für die anderen Arten von Einblendungen gibt es analoge Modifikatoren.

Eingefügte Größenmodifikatoren

Die folgenden Modifikatoren wenden eine Menge an Fenstereinfügungen an, indem sie die Größe der für die Komponente die Größe der Einsätze fest:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Die Startseite von „windowInsets“ wird als Breite angewendet (z. B. Modifier.width).

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Wendet die Endeseite von windowInsets auf die Breite an (z. B. Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Die Oberseite von „windowInsets“ wird als Höhe angewendet (z. B. Modifier.height).

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Die untere Seite von „windowInsets“ wird als Höhe angewendet (z. B. Modifier.height).

Diese Modifikatoren sind besonders nützlich, um die Größe eines Spacer festzulegen, das den Platz von Einzügen einnimmt:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Einblendung

Die Modifikatoren für den eingefügten Abstand (windowInsetsPadding und Hilfsfunktionen wie safeDrawingPadding) automatisch den Teil der Einfügungen verbrauchen, die als Abstand angewendet. Wenn Sie tiefer in den Kompositionbaum eindringen, wissen verschachtelte Modifikatoren für den Abstand von Einzügen und die Größe von Einzügen, dass ein Teil der Einzüge bereits von Modifikatoren für den Abstand von äußeren Einzügen belegt wurde. Sie vermeiden es, denselben Teil der Einzüge mehrmals zu verwenden, was zu zu viel zusätzlichem Platz führen würde.

Mit Modifikatoren für eingefügte Größen wird verhindert, dass ein Teil der Einfügungen mehrmals verwendet wird. falls bereits Insets aufgebraucht sind. Da sie jedoch ihre verwenden, verbrauchen sie selbst keine Einsätze.

Durch verschachtelte Padding-Modifikatoren wird daher automatisch die Größe des Paddings für jedes Composeable geändert.

Im gleichen LazyColumn-Beispiel wie zuvor wird die LazyColumn durch den imePadding-Modifikator in der Größe angepasst. Innerhalb von LazyColumn hat das letzte Element die Höhe des unteren Endes der Systemleisten:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Wenn die IME geschlossen ist, wird mit dem imePadding()-Modifikator kein Abstand angewendet, da die IME keine Höhe hat. Da mit dem imePadding()-Modifikator kein Abstand angewendet wird, werden keine Einzüge verwendet und die Höhe der Spacer entspricht der Größe der Unterseite der Systemleisten.

Wenn der IME geöffnet wird, werden die IME-Einfügungen so animiert, dass sie der Größe des IME entsprechen. Der imePadding()-Modifikator wendet jetzt einen Abstand unten an, um die Größe von LazyColumn, wenn der IME geöffnet wird. Wenn der imePadding()-Modifikator beginnt, den unteren Abstand anzuwenden, wird auch diese Menge an Einzügen verbraucht. Daher beginnt die Höhe des Spacer zu sinken, da ein Teil des Abstands für die Systemleisten bereits durch den imePadding()-Modifikator angewendet wurde. Sobald die Der imePadding()-Modifikator wendet einen größeren Abstand unten an als die Systembalken, ist die Höhe von Spacer null.

Wenn der IME geschlossen wird, erfolgen die Änderungen umgekehrt: Der Spacer beginnt, wird von einer Höhe von 0 aus maximiert, sobald imePadding() weniger angewendet wird als die der Systembalken, bis Spacer schließlich der Höhe des sobald der IME vollständig animiert ist.

Abbildung 2: Lazy-Spalte von Rand zu Rand mit TextField

Dieses Verhalten wird durch die Kommunikation zwischen allen windowInsetsPadding-Modifizierern erreicht und kann auf verschiedene andere Arten beeinflusst werden.

Modifier.consumeWindowInsets(insets: WindowInsets) verbraucht auch Einsätze wie für Modifier.windowInsetsPadding, sie gilt jedoch nicht für die aufgenommenen Einsätze als Padding. Dies ist in Kombination mit dem Einsatz Größenmodifikatoren, um Geschwistern anzuzeigen, dass eine bestimmte Anzahl von Einsätzen bereits verbraucht:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) verhält sich sehr gut ähnlich wie bei der Version mit einem WindowInsets-Argument, beliebige PaddingValues abrufen. Dies ist nützlich, um untergeordneten Elementen, wenn der Abstand oder die Abstände durch einen anderen Mechanismus als die Modifikatoren für eingefügten Abstand, z. B. ein gewöhnliches Modifier.padding oder eine feste Höhe Abstandshalter:

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

Wenn die Rohfenstereinfügungen ohne Verbrauch benötigt werden, verwenden Sie die Methode WindowInsets-Werte direkt oder verwenden Sie WindowInsets.asPaddingValues() für PaddingValues der Insets zurückgeben, die vom Verbrauch nicht betroffen sind. Aufgrund der nachfolgenden Vorbehalte sollte jedoch die Verwendung des Paddings für Fenstereinschnitte vorgezogen werden. Modifikatoren und Fenstereinblendungen Größenmodifikatoren wann immer möglich.

Phasen der Insets und Jetpack Compose-Phasen

Compose verwendet die zugrunde liegenden AndroidX Core APIs, um Einfügungen zu aktualisieren und zu animieren. die die zugrunde liegenden Plattform-APIs nutzen, die Einsätze verwalten. Aufgrund dieser Plattform Insets haben eine besondere Beziehung zu den Phasen von Jetpack. Schreiben:

Der Wert der Insets wird nach der Erstellungsphase, aber vor den Änderungen aktualisiert. Layoutphase. Das bedeutet, dass das Lesen des Werts von Insets in der Zusammensetzung verwendet im Allgemeinen einen Wert der Insets, der einen Frame zu spät kommt. Die auf dieser Seite beschriebenen integrierten Modifikatoren verzögern die Verwendung der Werte der Einzüge bis zur Layoutphase. So wird sichergestellt, dass die Einzugswerte im selben Frame verwendet werden, in dem sie aktualisiert werden.

Tastatur-IME-Animationen mit WindowInsets

Sie können Modifier.imeNestedScroll() auf einen scrollbaren Container anwenden, um die IME automatisch zu öffnen und zu schließen, wenn Sie zum Ende des Containers scrollen.

class WindowInsetsExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
                MyScreen()
            }
        }
    }
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon(imageVector = Icons.Filled.Add, contentDescription = "Add")
        }
    }
}

Animation, in der ein UI-Element nach oben und unten scrollt, um Platz für eine Tastatur zu schaffen
Abbildung 3 IME-Animationen

Eingefügte Unterstützung für Material 3-Komponenten

Für eine einfachere Bedienung gibt es viele der (androidx.compose.material3) Einsatzfelder selbst verarbeiten, je nachdem, wie die zusammensetzbaren Funktionen in Ihrer App platziert sind. gemäß den Materialspezifikationen.

Einfügen von Composeable-Elementen

Im Folgenden finden Sie eine Liste der Materialkomponenten, die Einzüge automatisch verarbeiten.

App-Leisten

Inhaltscontainer

Gerüst

Standardmäßig stellt Scaffold Einzüge als Parameter paddingValues bereit, die Sie verwenden können. Scaffold wendet die Einblendungen nicht auf Inhalte an. Dafür sind Sie selbst verantwortlich. So verwenden Sie diese Einzüge beispielsweise mit einem LazyColumn in einem Scaffold:

Scaffold { innerPadding ->
    // innerPadding contains inset information for you to use and apply
    LazyColumn(
        // consume insets as scaffold doesn't do it by default
        modifier = Modifier.consumeWindowInsets(innerPadding),
        contentPadding = innerPadding
    ) {
        items(count = 100) {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(50.dp)
                    .background(colors[it % colors.size])
            )
        }
    }
}

Standard-Einzüge überschreiben

Sie können den an das Composeable übergebenen Parameter windowInsets ändern, um das Verhalten des Composeables zu konfigurieren. Dieser Parameter kann ein anderer Fenstereinzug sein, der stattdessen angewendet werden soll, oder deaktiviert werden, indem eine leere Instanz übergeben wird: WindowInsets(0, 0, 0, 0).

Wenn Sie beispielsweise die Einzug-Verarbeitung für LargeTopAppBar deaktivieren möchten, setzen Sie den Parameter windowInsets auf eine leere Instanz:

LargeTopAppBar(
    windowInsets = WindowInsets(0, 0, 0, 0),
    title = {
        Text("Hi")
    }
)

Interoperabilität mit den Einblendungen des Google Maps-Systems

Möglicherweise müssen Sie die Standardeinfügungen überschreiben, wenn Ihr Bildschirm sowohl Ansichten als auch Erstellen Sie Code in derselben Hierarchie. In diesem Fall müssen Sie explizit welches die Einsätze aufnimmt und welches sie ignorieren soll.

Wenn Ihr äußerstes Layout beispielsweise ein Android-View-Layout ist, sollten Sie die Einzüge im View-System verwenden und für Compose ignorieren. Wenn Ihr äußerstes Layout ein Composeable ist, sollten Sie die Einzüge in Compose verwenden und die AndroidView-Composeables entsprechend ausrichten.

Standardmäßig werden von jedem ComposeView alle Inset-Assets auf der Verbrauchsebene WindowInsetsCompat verbraucht. Wenn Sie dieses Standardverhalten ändern möchten, setzen Sie ComposeView.consumeWindowInsets auf false.

Ressourcen