Layout personalizzati

In Compose, gli elementi dell'interfaccia utente sono rappresentati dalle funzioni componibili che emettono un pezzo di UI quando vengono richiamate, che viene poi aggiunto a un albero dell'interfaccia utente visualizzato sullo schermo. Ogni elemento UI 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 ha una dimensione, specificata come width e height.

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

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

  1. Misurare i bambini
  2. Decidere le proprie dimensioni
  3. Posiziona i suoi figli

Tre passaggi del layout dei nodi: misura i figli, decidi le dimensioni, posiziona i figli

L'utilizzo degli ambiti definisce quando puoi misurare e posizionare i tuoi figli. La misurazione di un layout può essere eseguita solo durante le passate di misurazione e layout e un elemento secondario può essere posizionato solo durante le passate 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 del layout

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

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

Visualizziamo un Text sullo schermo e controlliamo la distanza dalla parte superiore alla linea di base della prima riga di testo. È esattamente quello che fa il modificatore paddingFromBaseline, che implementiamo qui come esempio. Per farlo, utilizza il modificatore layout per posizionare manualmente il composable sullo schermo. Ecco il comportamento desiderato in cui viene impostato il padding superiore Text 24.dp:

Mostra la differenza tra la spaziatura interna normale dell'interfaccia utente, che imposta lo spazio tra gli elementi, e la spaziatura interna 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 Text rappresentato dal parametro misurabile chiamando measurable.measure(constraints).
  2. Specifichi le dimensioni del componibile chiamando il metodo layout(width, height), che fornisce anche un'espressione lambda utilizzata per posizionare gli elementi di wrapping. In questo caso, è l'altezza tra l'ultima linea di base e la spaziatura interna superiore aggiunta.
  3. Posiziona gli elementi di wrapping sullo schermo chiamando placeable.place(x, y). Se gli elementi di wrapping non vengono posizionati, non saranno visibili. La posizione ycorrisponde al riempimento 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 disporre più composable, utilizza invece il composable Layout. Questo elemento componibile ti consente di misurare e disporre i figli manualmente. Tutti i layout di livello superiore come Column e Row sono creati con il componente 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 dei figli che devono essere misurati e constraints sono i vincoli del genitore. 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 composable figlio sono vincolati dai vincoli Layout (senza i vincoli minHeight) e vengono posizionati in base a yPosition del composable precedente.

Ecco come verrebbe utilizzato questo 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 impilati uno sopra l'altro in una colonna.

Direzione del layout

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

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

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

Layout personalizzati in azione

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

Scopri di più

Per saperne di più sui layout personalizzati in Crea, consulta le seguenti risorse aggiuntive.

Video