Informazioni su WindowInsetsRulers

WindowInsets è l'API standard in 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 trasmettere WindowInsetsRulers predefiniti come SafeDrawing a Modifier.fitInside o Modifier.fitOutside per allineare i contenuti alle barre di sistema e al ritaglio del display o creare WindowInsetsRulers personalizzati.

Vantaggi di WindowInsetsRulers

  • Evita la complessità del consumo: opera durante la fase di posizionamento del layout. Ciò significa che bypassa completamente la catena di consumo degli inset e può sempre fornire le posizioni corrette e assolute 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 composable antenati consumano in modo errato gli inset.
  • Evita facilmente le barre di sistema: aiuta i contenuti dell'app a evitare le barre di sistema e l'intaglio del display e può essere più semplice dell'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 posizionali che fornisce non sono disponibili durante la fase di misurazione precedente.

Allineare i contenuti ai metodi di modifica

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

Ad esempio, per verificare che i contenuti dell'app evitino le barre di sistema e l'intaglio del 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 seguente tabella mostra l'aspetto dei contenuti dell'app con 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 StatusBar, CaptionBar, NavigationBar)

StatusBar

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

Alcune regole come Modifier.fitOutside su SafeDrawing e SystemBars restituiscono più regole. In questo caso, Android posiziona il composable utilizzando un righello da sinistra, in alto, a destra e in 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 delle didascalie con Modifier.fitInside

Analogamente, per verificare gli elementi in alto evitando la barra di stato e la barra dei sottotitoli codificati insieme a Modifier.fitInsider, passa un RectRuler che accetta 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 a righelli personalizzati. Ad esempio, considera il caso d'uso in cui un composable padre 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 composable figlio senza dover risolvere il problema nel composable padre upstream, 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 dell'inserimento IME causato da un elemento principale upstream nell'immagine a sinistra e l'utilizzo di righelli personalizzati per risolvere il problema a destra. Sotto il composable TextField viene mostrato un padding aggiuntivo perché il padding della barra di navigazione non è stato utilizzato dal genitore. L'elemento figlio viene posizionato nella posizione corretta nell'immagine a destra utilizzando un righello personalizzato come mostrato nell'esempio di codice precedente.

Verificare che i genitori siano vincolati

Per utilizzare in sicurezza WindowInsetsRulers, 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 di dimensioni nei composable principali.

Allo stesso modo, l'inserimento di un componente componibile che utilizza WindowInsetsRulers all'interno di un contenitore scorrevole come verticalScroll può causare un comportamento imprevisto, in quanto il contenitore scorrevole fornisce vincoli di altezza illimitati, che sono incompatibili con la logica del righello.