Layout personalizzati

In Compose, gli elementi dell'interfaccia utente sono rappresentati dalle funzioni componibili che, quando richiamati, emettono una parte dell'interfaccia utente che viene poi aggiunta a una struttura dell'interfaccia utente che viene visualizzata sullo schermo. Ogni elemento UI ha un elemento principale e potenzialmente molti elementi secondari. Ogni elemento si trova anche all'interno del relativo elemento principale, specificato come una posizione (x, y) e una dimensione, specificate come width e height.

I genitori definiscono i vincoli per gli elementi secondari. A un elemento viene chiesto di definire le proprie dimensioni entro questi vincoli. I vincoli limitano il numero minimo e massimo di width e height di un elemento. Se un elemento ha elementi secondari, può misurare ogni elemento secondario per determinarne le dimensioni. Una volta che un elemento determina e ne registra le proprie dimensioni, ha l'opportunità di definire come posizionare gli elementi secondari rispetto a se stesso, come descritto in dettaglio nella sezione Creazione di layout personalizzati.

La disposizione di ciascun nodo nell'albero dell'interfaccia utente è un processo in tre passaggi. Ogni nodo deve:

  1. Misura i bambini
  2. Stabilire la propria dimensione
  3. Posiziona i relativi figli

Tre passaggi per il layout dei nodi: misurare gli elementi secondari, decidere le dimensioni, posizionare gli elementi secondari

L'utilizzo degli ambiti definisce quando è possibile misurare e posizionare i figli. La misurazione di un layout può essere eseguita solo durante le operazioni di misurazione e layout e un elemento secondario può essere posizionato solo durante le fasi di misurazione del layout (e solo dopo che è stato misurato). A causa di 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 modalità di misurazione e disposizione di un elemento. Layout è una funzione lambda; i suoi parametri includono l'elemento che puoi misurare, passato come measurable, e i vincoli in entrata di questo componibile, passati come constraints. Un modificatore di layout personalizzato può avere questo aspetto:

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

Visualizza un elemento Text sullo schermo e controlliamo la distanza dalla parte superiore alla base di riferimento della prima riga di testo. Questa è esattamente la funzione del modificatore paddingFromBaseline, che stiamo implementando qui come esempio. A questo scopo, utilizza il modificatore layout per posizionare manualmente il componibile sullo schermo. Ecco il comportamento desiderato in cui la spaziatura interna superiore Text è impostata su 24.dp:

Mostra la differenza tra la normale spaziatura interna dell'interfaccia utente, che imposta lo spazio tra gli elementi, e la spaziatura interna del testo che imposta lo spazio da una base di riferimento a quella successiva.

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 nel codice:

  1. Nel parametro lambda measurable, misuri il valore Text rappresentato dal parametro misurabile tramite la chiamata di measurable.measure(constraints).
  2. Puoi specificare la dimensione del componibile chiamando il metodo layout(width, height), che fornisce anche una funzione lambda utilizzata per posizionare gli elementi aggregati. In questo caso, è l'altezza tra l'ultima base di riferimento e la spaziatura interna superiore aggiunta.
  3. Puoi posizionare gli elementi aggregati sullo schermo chiamando placeable.place(x, y). Se gli elementi aggregati non vengono posizionati, non saranno visibili. La posizione y corrisponde alla spaziatura interna superiore, ovvero la posizione della prima base di riferimento del testo.

Per verificare che funzioni come previsto, utilizza questo modificatore su 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))
    }
}

Anteprime multiple di elementi di testo; una mostra la normale spaziatura interna tra gli elementi, l'altra la spaziatura interna da una base di riferimento all'altra

Crea layout personalizzati

Il modificatore layout cambia solo il componibile di chiamata. Per misurare e layout più elementi componibili, utilizza invece l'elemento componibile Layout. Questo componibile consente di misurare e disporre i bambini manualmente. Tutti i layout di livello superiore, come Column e Row, sono creati con l'elemento componibile Layout.

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

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

In modo simile al modificatore layout, measurables è l'elenco dei figli che devono essere misurati e constraints sono i vincoli dell'elemento padre. Seguendo la stessa logica di prima, MyBasicColumn può essere implementato nel seguente modo:

@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
            }
        }
    }
}

Gli elementi componibili secondari sono vincolati dai vincoli Layout (senza i vincoli minHeight) e vengono posizionati in base al valore yPosition dell'elemento componibile precedente.

Ecco come verrà utilizzato l'elemento componibile 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 impilati uno sopra l'altro in una colonna.

Direzione layout

Cambia la direzione del layout di un componibile modificando la composizione locale di LocalLayoutDirection.

Se inserisci manualmente i componibili sullo schermo, LayoutDirection fa parte del LayoutScope del modificatore layout o del componibile Layout.

Quando usi layoutDirection, posiziona i componibili 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 Compose e osserva i layout personalizzati in azione in Esempi di Compose per creare layout personalizzati.

Scopri di più

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

Video