Layout vincolo in Scrivi

ConstraintLayout è un layout che consente di posizionare elementi componibili rispetto ad altri componibili sullo schermo. Si tratta di un'alternativa all'utilizzo di più Row, Column, Box e altri elementi di layout personalizzati nidificati. ConstraintLayout è utile per l'implementazione di layout più grandi con requisiti di allineamento più complicati.

Valuta la possibilità di utilizzare ConstraintLayout nei seguenti scenari:

  • evitare di nidificare più Column e Row per il posizionamento degli elementi sullo schermo in modo da migliorare la leggibilità del codice.
  • Posizionare i componibili rispetto ad altri componibili o posizionare i componibili in base a linee guida, barriere o catene.

Nel sistema di visualizzazione, ConstraintLayout era il metodo consigliato per creare layout complessi e di grandi dimensioni, poiché una gerarchia di visualizzazione piatta offriva prestazioni migliori rispetto alle viste nidificate. Tuttavia, questo non è un problema in Compose, che è in grado di gestire in modo efficiente le gerarchie di layout più profonde.

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 Compose funziona nel seguente modo utilizzando una DSL:

  • Crea riferimenti per ogni componibile in ConstraintLayout utilizzando createRefs() o createRefFor()
  • I vincoli vengono forniti utilizzando il modificatore constrainAs(), che prende il riferimento come parametro e consente di specificarne i vincoli nel corpo lambda.
  • I vincoli vengono specificati utilizzando linkTo() o altri metodi utili.
  • parent è un riferimento esistente che può essere utilizzato per specificare vincoli rispetto all'elemento componibile ConstraintLayout stesso.

Ecco un esempio di componibile che utilizza un elemento 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 del Button all'elemento principale con un margine di 16.dp e un Text alla fine del Button anch'esso con un margine di 16.dp.

Mostra un pulsante e un elemento di testo disposti in ConstraintLayout.

API disaccoppiata

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

In casi come questi, puoi utilizzare ConstraintLayout in un altro modo:

  1. Passa un elemento ConstraintSet come parametro a ConstraintLayout.
  2. Assegna i riferimenti creati in ConstraintSet ai componibili 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 i vincoli, puoi semplicemente trasmettere un ConstraintSet diverso.

ConstraintLayout concetti

ConstraintLayout contiene concetti quali linee guida, barriere e catene che possono aiutarti a posizionare gli elementi all'interno dell'elemento componibile.

Linee guida

Le linee guida sono dei piccoli aiutanti visivi con cui progettare i layout. I componibili possono essere vincolati a una linea guida. Le linee guida sono utili per posizionare gli elementi in un determinato elemento dp o percentage all'interno dell'elemento componibile principale.

Esistono due diversi tipi di linee guida: verticali e orizzontali. Le due 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 obbligatoria. Viene creato un riferimento che può essere utilizzato nel blocco Modifier.constrainAs().

Barriere

Le barriere fanno riferimento a più elementi componibili per creare una linea guida virtuale basata sul widget più estremo sul lato specificato.

Per creare una barriera, utilizza createTopBarrier() (o createBottomBarrier(), createEndBarrier(), createStartBarrier()) e fornisci i riferimenti che dovrebbero comporre la barriera.

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 offrono un comportamento di tipo gruppo su 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 ChainStyles diverse, che decidono come gestire lo spazio che circonda un componibile, ad esempio:

  • ChainStyle.Spread: lo spazio viene distribuito uniformemente tra tutti i componibili, compreso lo spazio libero prima del primo componibile e dopo l'ultimo.
  • ChainStyle.SpreadInside: lo spazio viene distribuito uniformemente tra tutti i componibili, senza alcuno spazio libero prima del primo componibile o dopo l'ultimo.
  • ChainStyle.Packed: lo spazio viene distribuito prima del primo e dopo l'ultimo, gli elementi componibili vengono componibili senza spazi l'uno dall'altro.

Scopri di più

Scopri di più su ConstraintLayout in Compose dalle API in azione nella sezione Esempi di Compose che utilizzano ConstraintLayout.