Scrivere le nozioni di base sul layout

Jetpack Compose rende molto più semplice la progettazione e la creazione dell'UI della tua app. Compose trasforma lo stato in elementi UI tramite:

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

Scrivi lo stato di trasformazione in UI tramite composizione, layout e disegno

Questo documento si concentra sul layout degli elementi, spiegando alcuni degli elementi costitutivi di Compose che ti aiutano a disporre gli elementi dell'interfaccia utente.

Obiettivi dei layout in Scrivi

L'implementazione di Jetpack Compose del sistema di layout 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 che descrive una parte della tua interfaccia utente. La funzione richiede un input e genera i contenuti visualizzati sullo schermo. Per ulteriori informazioni sui componenti componibili, consulta la documentazione del modello mentale.

Una funzione componibile potrebbe emettere diversi elementi UI. Tuttavia, se non fornisci indicazioni su come devono essere disposti, Compose può 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")
}

Senza indicazioni sulla loro disposizione, Compose impila gli elementi di testo uno sopra l'altro, rendendoli illeggibili:

Due elementi di testo disegnati uno sopra l'altro, rendendo il testo illeggibile

Compose fornisce una raccolta di layout pronti all'uso per aiutarti a organizzare gli elementi dell'interfaccia utente e ti consente di definire facilmente layout personalizzati e più specializzati.

Componenti del layout standard

In molti casi è sufficiente utilizzare gli elementi di layout standard di Scrivi.

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 layout a colonna, in modo che il testo sia leggibile

Allo stesso modo, utilizza Row per posizionare gli elementi orizzontalmente sullo schermo. Sia Column sia 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 un piccolo grafico accanto a una colonna di elementi di testo

Utilizza Box per posizionare gli elementi sopra un altro. Box supporta anche la configurazione dell'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 sull'altro

Spesso questi componenti di base sono tutto ciò di cui hai bisogno. Puoi scrivere la tua funzione componibile per combinare questi layout in un layout più elaborato che si adatta alla tua app.

Confronta tre componibili di layout semplice: colonna, riga e casella

Per impostare la posizione dei bambini all'interno di un elemento 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

Modello di layout

Nel modello di layout, la struttura ad albero dell'interfaccia utente è strutturata in un unico passaggio. A ogni nodo viene prima richiesto di misurare autonomamente i dati, quindi di misurare in modo ricorsivo i valori secondari, passando i vincoli di dimensione lungo l'albero ai figli. Quindi, i nodi delle foglie vengono dimensionati e posizionati, con le istruzioni risolte e le istruzioni di posizionamento che riprendono l'albero.

In breve, i genitori effettuano le misurazioni prima dei figli, ma hanno le proprie dimensioni e posizionati dopo i figli.

Valuta la seguente funzione SearchResult.

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

Questa funzione genera la seguente struttura di interfaccia utente.

SearchResult
  Row
    Image
    Column
      Text
      Text

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

  1. Al nodo radice Row viene chiesto di misurare.
  2. Il nodo radice Row chiede la misurazione al suo primo figlio, Image.
  3. Image è un nodo foglia (ossia non ha elementi secondari), quindi segnala una dimensione e restituisce le istruzioni di posizionamento.
  4. Il nodo radice Row chiede al suo secondo figlio, Column, di misurare.
  5. Il nodo Column chiede di misurare il suo primo elemento secondario Text.
  6. Il primo nodo Text è un nodo foglia, quindi indica le dimensioni e restituisce le istruzioni di posizionamento.
  7. Il nodo Column chiede alla seconda risorsa secondaria Text di misurare.
  8. Il secondo nodo Text è un nodo foglia, quindi indica una dimensione e restituisce le istruzioni di posizionamento.
  9. Ora che ha misurato, dimensionato e posizionato i nodi secondari del nodo Column, può determinare le proprie dimensioni e il proprio posizionamento.
  10. Ora che il nodo principale Row ha misurato, dimensionato e posizionato i relativi elementi secondari, può determinare le proprie dimensioni e il proprio posizionamento.

Ordine di misurazione, ridimensionamento e posizionamento nell'albero della UI dei risultati della Ricerca

Prestazioni

Compose raggiunge prestazioni elevate misurando i bambini solo una volta. La misurazione con pass singolo è ideale per le prestazioni, consentendo a Compose di gestire in modo efficiente gli alberi UI profondi. Se un elemento misurasse due volte il proprio figlio e quest'ultimo misurasse ciascuno dei propri figli due volte e così via, un solo tentativo di disporre di un'intera UI avrebbe dovuto fare molto, rendendo difficile mantenere prestazioni elevate dell'app.

Se per qualche motivo il layout richiede più misurazioni, Compose offre un sistema speciale, misure intrinseche. Per ulteriori informazioni su questa funzionalità, consulta la pagina Misurazioni intrinseche nei layout di Compose.

Poiché misurazione e posizionamento sono fasi secondarie distinte del passaggio di layout, qualsiasi modifica che influisce solo sul posizionamento degli elementi, non sulla misurazione, può essere eseguita separatamente.

Utilizzare i modificatori nei layout

Come descritto nella sezione sui modificatori di scrittura, puoi utilizzare i modificatori per decorare o migliorare i tuoi componenti. I modificatori sono essenziali per personalizzare il layout. Ad esempio, qui abbiamo concatenato 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 modificatori per modificare il modo in cui sono organizzate le immagini e quali aree rispondono all'input utente

Nel codice riportato sopra, puoi notare che le varie funzioni del modificatore vengono utilizzate insieme.

  • clickable reagisce con l'input dell'utente e mostra un'ondata.
  • padding aggiunge spazio a un elemento.
  • fillMaxWidth consente al riempimento componibile della larghezza massima assegnata al suo padre.
  • size() specifica la larghezza e l'altezza preferite di un elemento.

Layout a scorrimento

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

Per gli elenchi e gli elenchi pigri, consulta la documentazione relativa agli elenchi scritti.

Layout adattabili

Un layout dovrebbe essere progettato tenendo conto dei diversi orientamenti dello schermo e delle dimensioni del fattore di forma. Compose offre alcuni meccanismi per adattare facilmente i tuoi layout componibili a diverse configurazioni dello schermo.

Limitazioni

Per conoscere i vincoli generati dal publisher principale e progettare il layout di conseguenza, puoi utilizzare un BoxWithConstraints. I vincoli di misurazione si trovano nell'ambito della funzionalità lambda dei contenuti. Puoi utilizzare questi vincoli di misurazione per creare layout diversi per diverse configurazioni dello schermo:

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

Layout basati su slot

Compose fornisce un'ampia varietà di componibili basati su Material Design con la dipendenza androidx.compose.material:material (inclusa quando crei un progetto Compose in Android Studio) per semplificare la creazione dell'UI. Tutti gli elementi come Drawer, FloatingActionButton e TopAppBar sono tutti forniti.

I componenti Materiale fanno un uso intensivo delle API slot, un pattern di Compose introduce per introdurre un livello di personalizzazione oltre ai componenti componibili. Questo approccio rende i componenti più flessibili, in quanto accettano un elemento secondario che può configurarsi autonomamente invece di dover esporre ogni parametro di configurazione dell'elemento secondario. Gli slot lasciano uno spazio vuoto nell'interfaccia utente per consentire allo sviluppatore di riempire il campo a sua scelta. Ad esempio, queste sono le aree che puoi personalizzare in una TopAppBar:

Diagramma che mostra gli slot disponibili in una barra dell'app Componenti di materiale

In genere le componibili richiedono una funzione lambda componibile content ( content: @Composable () -> Unit). Le API Slot esponeno più parametri content per usi specifici. Ad esempio, TopAppBar consente di fornire i contenuti per title, navigationIcon e actions.

Ad esempio, Scaffold) consente di implementare un'interfaccia utente con la struttura del layout Material Design di base. Scaffoldfornisce aree per i componenti Material di primo livello più comuni, come TopAppBar, BottomAppBar, FloatingActionButton e Drawer. Utilizzando Scaffold, è facile assicurarsi che questi componenti siano posizionati e funzionano correttamente.

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

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