Nozioni di base sul layout di Scrivi

Jetpack Compose semplifica notevolmente la progettazione e la creazione dell'interfaccia utente della tua app. Compose trasforma lo stato in elementi dell'interfaccia utente tramite:

  1. Composizione degli elementi
  2. Layout degli elementi
  3. Disegno di elementi

Comporre lo stato di trasformazione nell'interfaccia utente tramite composizione, layout e disegno

Questo documento si concentra sul layout degli elementi e illustra alcuni degli elementi di base di Compose che ti aiutano a progettare gli elementi dell'interfaccia utente.

Scopi dei layout in Scrivi

L'implementazione del sistema di layout di Jetpack Compose ha due obiettivi principali:

Nozioni di base sulle funzioni componibili

Le funzioni componibili sono l'elemento di base di Compose. Una funzione componibile è una funzione che emette Unit e descrive una parte dell'interfaccia utente. La funzione accetta alcuni input e genera ciò che viene mostrato sullo schermo. Per ulteriori informazioni sui composabili, consulta la documentazione del modello mentale di composizione.

Una funzione componibile potrebbe emettere diversi elementi dell'interfaccia utente. Tuttavia, se non fornisci indicazioni su come devono essere disposti, Scrivi potrebbe disporre gli elementi in un modo che non ti piace. Ad esempio, questo codice genera due elementi di testo:

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

Se non fornisci indicazioni su come vuoi disporli, Scrivi impila gli elementi di testo uno sopra l'altro, rendendoli illeggibili:

Due elementi di testo sovrapposti che rendono il testo illeggibile

Compose fornisce una raccolta di layout pronti all'uso per aiutarti a organizzare gli elementi dell'interfaccia utente e semplifica la definizione di layout personalizzati e più specializzati.

Componenti del layout standard

In molti casi, puoi semplicemente utilizzare gli elementi di layout standard di Compose.

Utilizza Column per posizionare gli elementi verticalmente sullo schermo.

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

Due elementi di testo disposti in un layout a colonne, in modo che il testo sia leggibile

Analogamente, utilizza Row per posizionare gli elementi orizzontalmente sullo schermo. Sia Column che Row supportano la configurazione dell'allineamento degli elementi che contengono.

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

Mostra un layout più complesso, con una piccola grafica accanto a una colonna di elementi di testo

Utilizza Box per sovrapporre gli elementi. Box supporta anche la configurazione di un allineamento specifico degli elementi che contiene.

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

Mostra due elementi impilati uno sopra l'altro

Spesso questi elementi di base sono tutto ciò di cui hai bisogno. Puoi scrivere la tua funzione composable per combinare questi layout in un layout più elaborato adatto alla tua app.

Confronta tre composabili di layout semplici: colonna, riga e riquadro

Per impostare la posizione dei figli all'interno di un Row, imposta gli argomenti horizontalArrangement e verticalAlignment. Per un Column, imposta gli argomenti verticalArrangement e horizontalAlignment:

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

Gli elementi sono allineati a destra

Il modello di layout

Nel modello di layout, l'albero dell'interfaccia utente viene visualizzato in un unico passaggio. A ogni nodo viene prima chiesto di misurarsi, poi di misurare gli eventuali elementi secondari in modo ricorsivo, passando i vincoli di dimensione alla gerarchia. Poi, le dimensioni e il posizionamento dei nodi a foglia vengono impostati e le istruzioni per le dimensioni e il posizionamento risolte vengono ritrasmesse all'albero.

In breve, i genitori si misurano prima dei figli, ma le loro misure e la loro posizione vengono inserite dopo quelle dei figli.

Considera la seguente funzione SearchResult.

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

Questa funzione genera la seguente struttura dell'interfaccia utente.

SearchResult
  Row
    Image
    Column
      Text
      Text

Nell'esempio SearchResult, il layout dell'albero dell'interfaccia utente segue questo ordine:

  1. Viene chiesto di misurare il nodo principale Row.
  2. Il nodo principale Row chiede al suo primo elemento secondario, Image, di eseguire la misurazione.
  3. Image è un nodo foglia (ovvero non ha elementi secondari), pertanto riporta una dimensione e restituisce le istruzioni di posizionamento.
  4. Il nodo principale Row chiede al secondo nodo figlio, Column, di eseguire la misurazione.
  5. Il nodo Column chiede al primo elemento secondario Text di eseguire la misurazione.
  6. Il primo nodo Text è un nodo foglia, quindi riporta una dimensione e restituisce istruzioni di posizionamento.
  7. Il nodo Column chiede al secondo nodo figlio Text di eseguire la misurazione.
  8. Il secondo nodo Text è un nodo foglia, quindi riporta una dimensione e restituisce le istruzioni di posizionamento.
  9. Ora che il nodo Column ha misurato, definito le dimensioni e posizionato i suoi elementi secondari, può determinare le proprie dimensioni e il proprio posizionamento.
  10. Ora che il nodo principale Row ha misurato, definito le dimensioni e posizionato i propri nodi secondari, può determinare le proprie dimensioni e il proprio posizionamento.

Ordinamento di misurazione, dimensioni e posizionamento nella struttura dell'interfaccia utente dei risultati di ricerca

Prestazioni

Compose raggiunge prestazioni elevate misurando i bambini una sola volta. La misurazione con un solo passaggio è ottima per le prestazioni, in quanto consente a Compose di gestire in modo efficiente gli alberi dell'interfaccia utente complessi. Se un elemento misurasse il proprio elemento figlio due volte e questo misurasse ciascuno dei suoi elementi figlio due volte e così via, un singolo tentativo di progettare un'intera UI richiederebbe molto lavoro, rendendo difficile mantenere il rendimento dell'app.

Se per qualche motivo il layout richiede più misurazioni, Compose offre un sistema speciale, le misure intrinseche. Puoi scoprire di più su questa funzionalità in Misurazioni intrinseche nei layout di Compose.

Poiché la misurazione e il posizionamento sono sottofasi distinte del passaggio del layout, eventuali modifiche che influiscono solo sul posizionamento degli elementi, non sulla misurazione, possono essere eseguite separatamente.

Utilizzare i modificatori nei layout

Come discusso in Modificatori di composizione, puoi utilizzare i modificatori per decorare o aumentare i tuoi composabili. I modificatori sono essenziali per personalizzare il layout. Ad esempio, qui abbiamo collegato diversi modificatori per personalizzare ArtistCard:

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

Un layout ancora più complesso, che utilizza i modificatori per cambiare il modo in cui le immagini sono disposte e le aree che rispondono all'input dell'utente

Nel codice riportato sopra, noterai diverse funzioni di modificatore utilizzate insieme.

  • clickable fa in modo che un composable reagisca all'input dell'utente e mostri un'eco.
  • padding inserisce uno spazio intorno a un elemento.
  • fillMaxWidth fa in modo che il composable raggiunga la larghezza massima assegnata dall'elemento principale.
  • size() specifica la larghezza e l'altezza preferite di un elemento.

Layout scorrevoli

Scopri di più sui layout scorrevoli nella documentazione relativa ai gesti di composizione.

Per gli elenchi e gli elenchi lazy, consulta la documentazione relativa alla composizione degli elenchi.

Layout adattabili

Un layout deve essere progettato tenendo conto di diversi orientamenti dello schermo e dimensioni del fattore di forma. Compose offre già alcuni meccanismi per facilitare l'adattamento dei layout composable a varie configurazioni dello schermo.

Vincoli

Per conoscere le limitazioni provenienti dal componente principale e progettare il layout di conseguenza, puoi utilizzare un BoxWithConstraints. I limiti di misurazione si trovano nell'ambito della lambda dei contenuti. Puoi utilizzare queste limitazioni di misurazione per comporre layout diversi per configurazioni dello schermo diverse:

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

Layout basati su slot

Compose fornisce una vasta gamma di composable basati su Material Design con la dipendenza androidx.compose.material:material (inclusa durante la creazione di un progetto Compose in Android Studio) per semplificare la creazione dell'interfaccia utente. Sono forniti elementi come Drawer, FloatingActionButton, e TopAppBar.

I componenti Material fanno un uso intensivo delle API di slot, un pattern introdotto da Compose per aggiungere un livello di personalizzazione ai composabili. Questo approccio consente di creare componenti più flessibili, in quanto accettano un elemento secondario che può configurarsi da solo, anziché dover esporre ogni parametro di configurazione del componente secondario. Gli slot lasciano uno spazio vuoto nell'interfaccia utente che lo sviluppatore può riempire a piacere. Ad esempio, questi sono gli slot che puoi personalizzare in un TopAppBar:

Un diagramma che mostra gli slot disponibili in una barra delle app di Material Components

I composabili in genere accettano un lambda composable ( content: @Composable () -> Unit). Le API di slot espongono più parametri content per utilizzi specifici.content Ad esempio, TopAppBar ti consente di fornire i contenuti per title, navigationIcon e actions.

Ad esempio, Scaffold consente di implementare un'interfaccia utente con la struttura di layout di base di Material Design. Scaffoldfornisce gli slot per i componenti Material di primo livello più comuni, come TopAppBar, BottomAppBar, FloatingActionButton, e Drawer. Con Scaffold è facile assicurarsi che questi componenti siano posizionati correttamente e collaborino in modo corretto.

L'app di esempio JetNews, che utilizza Scaffold per posizionare più elementi

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}