Informazioni su WindowInsetsRulers

WindowInsets è l'API standard di Jetpack Compose per la gestione delle aree dello schermo parzialmente o completamente oscurate dalla UI di sistema. Queste aree includono la barra di stato, la barra di navigazione e la tastiera sullo schermo. In alternativa, puoi passare WindowInsetsRulers predefiniti come SafeDrawing a Modifier.fitInside o Modifier.fitOutside per allineare i contenuti alle barre di sistema e al ritaglio display o creare WindowInsetsRulers personalizzati.

Vantaggi di WindowInsetsRulers

  • Evita la complessità del consumo: opera durante la fase di posizionamento del layout. Ciò significa che ignora completamente la catena di consumo degli inserti e può sempre fornire le posizioni assolute corrette delle barre di sistema e dei ritagli del display, indipendentemente da ciò che hanno fatto i layout principali. L'utilizzo dei metodi Modifier.fitInside o Modifier.fitOutside è utile per risolvere i problemi quando i composabili predecessori utilizzano gli inset in modo errato.
  • Evita facilmente le barre di sistema: aiuta i contenuti della tua app a evitare le barre di sistema e il ritaglio display e può essere più semplice rispetto all'utilizzo diretto di WindowInsets.
  • Altamente personalizzabile: gli sviluppatori possono allineare i contenuti a righelli personalizzati e avere un controllo preciso sui layout con layout personalizzati.

Svantaggi di WindowInsetsRulers

  • Non può essere utilizzato per la misurazione: poiché opera durante la fase di posizionamento, le informazioni sulla posizione che fornisce non sono disponibili durante la fase di misurazione precedente.

Allinea i contenuti con i metodi Modifier

Modifier.fitInside consente alle app di allineare i contenuti alle barre di sistema e ai ritagli display. Può essere utilizzato al posto di WindowInsets. Modifier.fitOutside è solitamente l'inverso di Modifier.fitInside.

Ad esempio, per verificare che i contenuti dell'app evitino le barre di sistema e il ritaglio display, puoi utilizzare fitInside(WindowInsetsRulers.safeDrawing.current).

@Composable
fun FitInsideDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc...
            .fitInside(WindowInsetsRulers.SafeDrawing.current)
    )
}

La tabella seguente mostra l'aspetto dei contenuti della tua app con i righelli predefiniti con Modifier.fitInside o Modifier.fitOutside.

Tipo di righello predefinito

Modifier.fitInside

Modifier.fitOutside

DisplayCutout

Ime

N/D

NavigationBars

SafeDrawing

N/A (utilizza invece StatusBar, CaptionBar, NavigationBar)

StatusBar

L'utilizzo di Modifier.fitInside e Modifier.fitOutside richiede che i componibili siano vincolati. Ciò significa che devi definire modificatori come Modifier.size o Modifier.fillMaxSize.

Alcuni righelli come Modifier.fitOutside su SafeDrawing e SystemBars restituiscono più righelli. In questo caso, Android posiziona il Composable utilizzando un righello da sinistra, dall'alto, da destra e dal basso.

Evita l'IME con Modifier.fitInside

Per gestire gli elementi inferiori con un IME con Modifier.fitInside, passa un RectRuler che accetta il valore più interno di NavigationBar e Ime.

@Composable
fun FitInsideWithImeDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.NavigationBars.current,
                    WindowInsetsRulers.Ime.current
                )
            )
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier.align(Alignment.BottomStart).fillMaxWidth()
        )
    }
}

Evita la barra di stato e la barra dei sottotitoli con Modifier.fitInside

Analogamente, per verificare che gli elementi superiori evitino la barra di stato e la barra dei sottotitoli insieme a Modifier.fitInside, passa un RectRuler che accetti il valore più interno di StatusBars e CaptionBar.

@Composable
fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) {
    Box(
        modifier = modifier
            .fillMaxSize()
            .fitInside(
                RectRulers.innermostOf(
                    WindowInsetsRulers.StatusBars.current,
                    WindowInsetsRulers.CaptionBar.current
                )
            )
    )
}

Crea WindowInsetsRulers personalizzato

Puoi allineare i contenuti ai righelli personalizzati. Ad esempio, considera il caso d'uso in cui un composable principale gestisce in modo errato gli inset, causando problemi di spaziatura interna in un elemento secondario downstream. Sebbene questo problema possa essere risolto in altri modi, ad esempio utilizzando Modifier.fitInside, puoi anche creare un righello personalizzato per allineare con precisione il componibile secondario senza dover risolvere il problema nel componente principale a monte, come mostrato nell'esempio e nel video seguenti:

@Composable
fun WindowInsetsRulersDemo(modifier: Modifier) {
    Box(
        contentAlignment = BottomCenter,
        modifier = modifier
            .fillMaxSize()
            // The mistake that causes issues downstream, as .padding doesn't consume insets.
            // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars),
            // assume it's difficult to identify this issue to see how WindowInsetsRulers can help.
            .padding(WindowInsets.navigationBars.asPaddingValues())
    ) {
        TextField(
            value = "Demo IME Insets",
            onValueChange = {},
            modifier = modifier
                // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child
                // Composable without having to fix the parent upstream.
                .alignToSafeDrawing()

            // .imePadding()
            // .fillMaxWidth()
        )
    }
}

fun Modifier.alignToSafeDrawing(): Modifier {
    return layout { measurable, constraints ->
        if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) {
            val placeable = measurable.measure(constraints)
            val width = placeable.width
            val height = placeable.height
            layout(width, height) {
                val bottom = WindowInsetsRulers.SafeDrawing.current.bottom
                    .current(0f).roundToInt() - height
                val right = WindowInsetsRulers.SafeDrawing.current.right
                    .current(0f).roundToInt()
                val left = WindowInsetsRulers.SafeDrawing.current.left
                    .current(0f).roundToInt()
                measurable.measure(Constraints.fixed(right - left, height))
                    .place(left, bottom)
            }
        } else {
            val placeable = measurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

Il seguente video mostra un esempio di consumo problematico degli inserti IME causato da un elemento principale upstream nell'immagine a sinistra e l'utilizzo di righelli personalizzati per risolvere il problema a destra. Viene mostrato un padding aggiuntivo sotto il composable TextField perché il padding della barra di navigazione non è stato utilizzato dall'elemento principale. Il figlio viene posizionato nella posizione corretta nell'immagine a destra utilizzando un righello personalizzato, come mostrato nell'esempio di codice precedente.

Verifica che i genitori siano vincolati

Per utilizzare WindowInsetsRulers in sicurezza, assicurati che il genitore fornisca vincoli validi. Gli elementi principali devono avere una dimensione definita e non possono dipendere dalla dimensione di un elemento secondario che utilizza WindowInsetsRulers. Utilizza fillMaxSize o altri modificatori delle dimensioni sui composabili principali.

Allo stesso modo, l'inserimento di un composable che utilizza WindowInsetsRulers all'interno di un contenitore scorrevole come verticalScroll può causare un comportamento imprevisto, poiché il contenitore scorrevole fornisce vincoli di altezza illimitati, che non sono compatibili con la logica del righello.