Layout vincolo in Scrivi

ConstraintLayout è un layout che ti consente di posizionare i composabili rispetto ad altri composabili sullo schermo. È un'alternativa all'utilizzo di più elementi personalizzati di layout Row, Column, Box e altri elementi di layout personalizzati nidificati. ConstraintLayout è utile per implementare layout più grandi con requisiti di allineamento più complicati.

Valuta la possibilità di utilizzare ConstraintLayout nei seguenti scenari:

  • Per evitare di nidificare più Column e Row per posizionare gli elementi sullo schermo, migliora la leggibilità del codice.
  • Per posizionare i composabili rispetto ad altri composabili o per posizionare i composabili in base a linee guida, barriere o catene.

Nel sistema di visualizzazione, ConstraintLayout era il metodo consigliato per creare layout grandi e complessi, in quanto una gerarchia di visualizzazioni piatta era migliore per il rendimento rispetto alle visualizzazioni nidificate. Tuttavia, questo non è un problema in Compose, che è in grado di gestire in modo efficiente gerarchie di layout complesse.

Inizia a utilizzare ConstraintLayout

Per utilizzare ConstraintLayout in Compose, devi aggiungere questa dipendenza in build.gradle (oltre alla configurazione di Compose):

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

ConstraintLayout in Scrittura funziona nel seguente modo utilizzando un DSL:

  • Crea riferimenti per ogni composable in ConstraintLayout utilizzando createRefs() o createRefFor()
  • I vincoli vengono forniti utilizzando il modificatore constrainAs(), che prende il riferimento come parametro e ti consente di specificare i relativi vincoli nel corpo della funzione lambda.
  • I vincoli vengono specificati utilizzando linkTo() o altri metodi utili.
  • parent è un riferimento esistente che può essere utilizzato per specificare vincoli nei confronti del componibile ConstraintLayout stesso.

Ecco un esempio di composable che utilizza un ConstraintLayout:

@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        // Create references for the composables to constrain
        val (button, text) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // Assign reference "button" to the Button composable
            // and constrain it to the top of the ConstraintLayout
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text("Button")
        }

        // Assign reference "text" to the Text composable
        // and constrain it to the bottom of the Button composable
        Text(
            "Text",
            Modifier.constrainAs(text) {
                top.linkTo(button.bottom, margin = 16.dp)
            }
        )
    }
}

Questo codice vincola la parte superiore di Button all'elemento principale con un margine di 16.dp e un Text alla parte inferiore di Button, sempre con un margine di 16.dp.

Mostra un pulsante e un elemento di testo disposti in un layout vincolato

API disaccoppiata

Nell'esempio ConstraintLayout, i vincoli sono specificati in linea, con un modificatore nel composable a cui vengono applicati. Tuttavia, in alcune situazioni è preferibile disaccoppiare i vincoli dai layout a cui si applicano. Ad esempio, potresti voler cambiare i vincoli in base alla configurazione dello schermo o animare tra due insiemi di vincoli.

Per casi come questi, puoi utilizzare ConstraintLayout in un modo diverso:

  1. Passa un ConstraintSet come parametro a ConstraintLayout.
  2. Assegna i riferimenti creati in ConstraintSet ai composabili utilizzando il modificatore layoutId.

@Composable
fun DecoupledConstraintLayout() {
    BoxWithConstraints {
        val constraints = if (minWidth < 600.dp) {
            decoupledConstraints(margin = 16.dp) // Portrait constraints
        } else {
            decoupledConstraints(margin = 32.dp) // Landscape constraints
        }

        ConstraintLayout(constraints) {
            Button(
                onClick = { /* Do something */ },
                modifier = Modifier.layoutId("button")
            ) {
                Text("Button")
            }

            Text("Text", Modifier.layoutId("text"))
        }
    }
}

private fun decoupledConstraints(margin: Dp): ConstraintSet {
    return ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        constrain(button) {
            top.linkTo(parent.top, margin = margin)
        }
        constrain(text) {
            top.linkTo(button.bottom, margin)
        }
    }
}

Poi, quando devi modificare le limitazioni, puoi semplicemente passare un valore ConstraintSet diverso.

Concetti di ConstraintLayout

ConstraintLayout contiene concetti come linee guida, barriere e catene che possono aiutarti a posizionare gli elementi all'interno del tuo Composable.

Linee guida

Le linee guida sono piccoli ausili visivi per progettare i layout. I composabili possono essere vincolati a una linea guida. Le linee guida sono utili per posizionare gli elementi in un determinato dp o percentage all'interno del composable principale.

Esistono due tipi diversi di linee guida, verticali e orizzontali. Le due linee orizzontali sono top e bottom, mentre le due verticali sono start e end.

ConstraintLayout {
    // Create guideline from the start of the parent at 10% the width of the Composable
    val startGuideline = createGuidelineFromStart(0.1f)
    // Create guideline from the end of the parent at 10% the width of the Composable
    val endGuideline = createGuidelineFromEnd(0.1f)
    //  Create guideline from 16 dp from the top of the parent
    val topGuideline = createGuidelineFromTop(16.dp)
    //  Create guideline from 16 dp from the bottom of the parent
    val bottomGuideline = createGuidelineFromBottom(16.dp)
}

Per creare una linea guida, utilizza createGuidelineFrom* con il tipo di linea guida richiesto. Viene creato un riferimento che può essere utilizzato nel blocco Modifier.constrainAs().

Barriere

Barriere fa riferimento a più composabili per creare una linea guida virtuale in base al widget più estremo sul lato specificato.

Per creare una barriera, utilizza createTopBarrier() (o createBottomBarrier(), createEndBarrier(), createStartBarrier()) e fornisci i riferimenti che devono costituirla.

ConstraintLayout {
    val constraintSet = ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        val topBarrier = createTopBarrier(button, text)
    }
}

La barriera può quindi essere utilizzata in un blocco Modifier.constrainAs().

Catene

Le catene forniscono un comportamento simile a quello di un gruppo in un singolo asse (orizzontale o verticale). L'altro asse può essere vincolato in modo indipendente.

Per creare una catena, utilizza createVerticalChain o createHorizontalChain:

ConstraintLayout {
    val constraintSet = ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread)
        val horizontalChain = createHorizontalChain(button, text)
    }
}

La catena può quindi essere utilizzata nel blocco Modifier.constrainAs().

Una catena può essere configurata con diversi ChainStyles, che determinano come gestire lo spazio che circonda un composable, ad esempio:

  • ChainStyle.Spread: lo spazio viene distribuito in modo uniforme tra tutti i composabili, incluso lo spazio libero prima del primo composable e dopo l'ultimo composable.
  • ChainStyle.SpreadInside: lo spazio viene distribuito in modo uniforme tra tutti i composabili, senza spazi vuoti prima del primo composable o dopo l'ultimo.
  • ChainStyle.Packed: lo spazio viene distribuito prima del primo e dopo l'ultimo composable, i composable sono raggruppati senza spazi tra loro.

Scopri di più

Scopri di più su ConstraintLayout in Compose dalle API in azione nei campioni di Compose che utilizzano ConstraintLayout.