Layout personalizzati

In Compose, gli elementi dell'interfaccia utente sono rappresentati dalle funzioni composable che emettono un frammento di interfaccia utente quando vengono richiamate, che viene poi aggiunto a una struttura ad albero dell'interfaccia utente visualizzata sullo schermo. Ogni elemento dell'interfaccia utente ha un elemento padre e potenzialmente molti elementi secondari. Ogni elemento si trova anche all'interno del relativo elemento principale, specificato come posizione (x, y) e come dimensione, specificata come width e height.

I gruppi principali definiscono le limitazioni per i propri elementi secondari. A un elemento viene chiesto di definire le sue dimensioni entro questi vincoli. I vincoli limitano i valori minimi e massimi width e height di un elemento. Se un elemento ha elementi secondari, può misurare ciascuno di questi per determinare le relative dimensioni. Una volta che un elemento determina e riporta le proprie dimensioni, ha la possibilità di definire come posizionare i propri elementi secondari rispetto a se stesso, come descritto in dettaglio in Creare layout personalizzati.

La disposizione di ogni nodo nell'albero dell'interfaccia utente è una procedura in tre passaggi. Ogni nodo deve:

  1. Misura eventuali elementi secondari
  2. Decidere le proprie dimensioni
  3. Inserisci i relativi elementi secondari

Tre passaggi per la disposizione dei nodi: misura i bambini, decidi le dimensioni, posiziona i bambini

L'utilizzo degli ambiti definisce quando puoi misurare e posizionare i tuoi figli. La misurazione di un layout può essere eseguita solo durante i passaggi di misurazione e di layout, mentre un elemento secondario può essere posizionato solo durante i passaggi di layout (e solo dopo essere stato misurato). A causa degli ambiti di Compose come MeasureScope e PlacementScope, questo viene applicato in fase di compilazione.

Utilizzare il modificatore di layout

Puoi utilizzare il modificatore layout per modificare la misurazione e la disposizione di un elemento. Layout è una funzione lambda; i suoi parametri includono l'elemento che puoi misurare, tramesso come measurable, e i vincoli in entrata del composable, trasmessi come constraints. Un modificatore di layout personalizzato può avere il seguente aspetto:

fun Modifier.customLayoutModifier() =
    layout { measurable, constraints ->
        // ...
    }

Mostriamo un Text sullo schermo e controlliamo la distanza dalla parte superiore alla linea di base della prima riga di testo. È esattamente ciò che fa il modificatore paddingFromBaseline, che stiamo implementando qui come esempio. A questo scopo, utilizza il modificatore layout per posizionare manualmente il composable sullo schermo. Ecco il comportamento desiderato quando il Text padding superiore è impostato su 24.dp:

Mostra la differenza tra il normale spaziatura dell'interfaccia utente, che imposta lo spazio tra gli elementi, e lo spazio del testo che imposta lo spazio da una linea di base all'altra

Ecco il codice per produrre questa spaziatura:

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // Check the composable has a first baseline
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

Ecco cosa succede in questo codice:

  1. Nel parametro lambda measurable, misuri il Text rappresentato dal parametro misurabile chiamando measurable.measure(constraints).
  2. Specifichi le dimensioni del componibile chiamando il layout(width, height) metodo, che fornisce anche un lambda utilizzato per posizionare gli elementi con wrapping. In questo caso, si tratta dell'altezza tra l'ultima linea di base e la spaziatura interna superiore aggiunta.
  3. Posiziona gli elementi a capo sullo schermo chiamando placeable.place(x, y). Se gli elementi a capo non vengono posizionati, non saranno visibili. La posizione ycorrisponde al padding superiore, ovvero alla posizione della prima linea di base del testo.

Per verificare che funzioni come previsto, utilizza questo modificatore su un Text:

@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

@Preview
@Composable
fun TextWithNormalPaddingPreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.padding(top = 32.dp))
    }
}

Più anteprime di elementi di testo: una mostra la spaziatura ordinaria tra gli elementi, l'altra mostra la spaziatura da una linea di base all'altra

Creare layout personalizzati

Il modificatore layout modifica solo il composable di chiamata. Per misurare e creare il layout di più composabili, utilizza invece il composable Layout. Questo composable consente di misurare e disporre manualmente gli elementi secondari. Tutti i layout di livello superiore come Column e Row sono creati con il composable Layout.

Creiamo una versione molto semplice di Column. La maggior parte dei layout personalizzati segue questo pattern:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
        // ...
    }
}

Analogamente al modificatore layout, measurables è l'elenco degli elementi secondari che devono essere misurati e constraints sono i vincoli dell'elemento principale. Seguendo la stessa logica di prima, MyBasicColumn può essere implementato come segue:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

I composabili secondari sono vincolati dai vincoli Layout (senza i vincoli minHeight) e vengono posizionati in base a yPosition del composable precedente.

Ecco come verrà utilizzato il composable personalizzato:

@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

Diversi elementi di testo sovrapposti in una colonna.

Direzione del layout

Modifica l'orientamento del layout di un composable modificando la proprietà LocalLayoutDirection composition local.

Se posizioni manualmente gli elementi composibili sullo schermo, LayoutDirection fa parte di LayoutScope del modificatore layout o dell'elemento componibile Layout.

Quando utilizzi layoutDirection, inserisci i composabili utilizzando place. A differenza del metodo placeRelative, place non cambia in base alla direzione del layout (da sinistra a destra o da destra a sinistra).

Layout personalizzati in azione

Scopri di più su layout e modificatori in Layout di base in Componi e visualizza i layout personalizzati in azione in Esempi di Componi che creano layout personalizzati.

Scopri di più

Per scoprire di più sui layout personalizzati in Scrivi, consulta le seguenti risorse aggiuntive.

Video