Constraint Layout in Jetpack Compose

Constraint Layout in Jetpack Compose

introduction

I'll show you how to use Constraint Layout to build complex UIs with less code.
In Jetpack Compose, ConstraintLayoutthere is indeed a powerful layout system that allows you to create complex and responsive layouts using the relative position of assemblies.

ConstraintLayoutHere are some of the advantages of using Jetpack Compose :

  1. Complex Layouts : Ideal
    when you need to create complex and custom layouts that cannot be easily achieved using simple rows and columns. ConstraintLayoutThis complex layout can be achieved by using ConstraintLayoutprecisely positioned components instead of nesting multiple rows and columns.

  2. Relative positioning :
    You can use constraints to position components relatively. This is useful when you want one assembly to be adjacent or aligned with another assembly. This relative placement can help you achieve more complex UI designs.

  3. Performance considerations :
    Although performance issues in nested view hierarchies are more important in View systems, Jetpack Compose is designed to handle deep layout hierarchies efficiently. Therefore, ConstraintLayoutthe main advantage in Compose is not performance optimization, but improving code readability and maintaining a clear structure in the layout.

Get started with ConstraintLayout

Add dependencies to build.gradle(:app):

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

Use ConstraintLayout :

  1. Create a reference: Create a reference for each assembly you want to position in the ConstraintLayout. You can create a reference using createRefs()a function or through a separate assembly createRefFor().

  2. Specify constraints: Use the modifier on each assembly you want to target constrainAs(). Pass the corresponding reference as a parameter constrainAs().

  3. In the lambda, use linkTo()the etc. method to specify constraints for the assembly.

  4. Using "parent" references: You can also use parent references to specify constraints relative to the ConstraintLayout itself. This positions the assembly relative to the layout boundaries.

  5. ConstraintSet: ConstraintSetAllows you to create and save constraints, applying them to existing ones ConstraintLayout.

For these cases you can use it in different ways ConstraintLayout:

  1. will be ConstraintSetpassed as a parameter ConstraintLayout.

  2. Use layoutIdthe modifier to ConstraintSetassign the reference created in to the assembly.

For example :

  1. Use the createRefs, constrainAsand linkTomodifiers.
@Composable
fun ConstraintLayoutContent() {
    
    
     ConstraintLayout(Modifier.padding(16.dp)) {
    
    
        // Create references for the composables to constrain
        val (button, text, progressbar) = 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)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
            }
        ) {
    
    
            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)
            }
        )
        LinearProgressIndicator(
            progress = 0.60f,
            modifier = Modifier.height(10.dp).constrainAs(progressbar) {
    
    
                start.linkTo(text.end, margin = 8.dp)
                top.linkTo(text.top)
                end.linkTo(parent.end)
                bottom.linkTo(text.bottom)
            }
        )
    }
}

Output

  1. Use createRefFor, linkToand ConstraintSetto build dynamic layouts.
@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, modifier = Modifier.padding(16.dp)) {
    
    
            Button(
                onClick = {
    
     /* Do something */ },
                modifier = Modifier.layoutId("button")
            ) {
    
    
                Text("Button")
            }

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

            LinearProgressIndicator(
                progress = 0.60f,
                modifier = Modifier.height(10.dp).layoutId("progress_bar")
            )
        }
    }
}

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

         val progressBar = createRefFor("progress_bar")

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

Output the results of vertical and horizontal constraints simultaneously

ConstraintLayoutConcept

Guideline :
A guideline is a small visual aid for designing layouts. Assemblies can be constrained to reference lines. dpGuides are useful for positioning elements at specific or percentage positions within a parent assembly .

ConstraintLayout allows you to define horizontal and vertical guides, which are guides used for alignment or spacing. Guides are especially helpful when creating responsive layouts that adapt to different screen sizes and orientations.

The two horizontal guides are top and bottom, and the two vertical guides are start and 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)
}

To create a guide, use createGuidelineFrom*and the desired guide type. This creates a reference that can be Modifier.constrainAs()used within the block.

Note: Consider using the Spacer component to achieve similar effects in rows and columns.

Barrier :
Barriers reference multiple assemblies to create virtual guidance lines based on the most extreme components on a given side. They allow you to create dynamic barriers that adjust their position based on the visibility of certain components. This is useful for handling situations where the visibility of one assembly affects the layout of other assemblies.

To create a barrier, use createTopBarrier()(or: createBottomBarrier()、createEndBarrier()、createStartBarrier()) and provide a reference that should form the barrier.

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

        val topBarrier = createTopBarrier(button, text)
    }
}

Barrier can be Modifier.constrainAs()used within blocks.

Note: Consider using intrinsic measurement methods to achieve similar effects in rows and columns.

Chains :
Chains provide group-like behavior on a single axis (horizontal or vertical). The other axis can be constrained independently.

Chains are used to align and distribute assemblies in rows or columns. You can create linear chains where assemblies are connected to each other and the chains can be evenly spaced or aligned as desired.

To create a chain, use createVerticalChainor 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)
    }
}

Chains can Modifier.constrainAs()be used in blocks.

ChainStylesChains can be configured using different ones , which determine how the space around the assembly is treated, for example:

  • ChainStyle.Spread: Space is evenly distributed between all assemblies, including free space before the first assembly and after the last assembly.
  • ChainStyle.SpreadInside: Space is evenly distributed among all assemblies, without any free space before the first assembly or after the last assembly.
  • ChainStyle.Packed: The space is distributed before the first assembly and after the last assembly, the assemblies are packed closely together with no space between each other.

Note: Consider using traditional Rowsand Columnsdifferent Arrangementsto achieve a similar effect to chains in ConstraintLayout.

Example :

@Composable
fun IndianFlagScreen() {
    
    
    val constraints = ConstraintSet {
    
    
        val orangeBox = createRefFor("orangebox")
        val greenBox = createRefFor("greenbox")
        val circle = createRefFor("circle")

        constrain(orangeBox) {
    
    
            top.linkTo(parent.top)
            start.linkTo(parent.start)
            end.linkTo(parent.end)
            bottom.linkTo(circle.top)
            width = Dimension.fillToConstraints
            height = Dimension.value(260.dp)
        }
        constrain(circle) {
    
    
            top.linkTo(parent.top)
            bottom.linkTo(parent.bottom)
            start.linkTo(parent.start)
            end.linkTo(parent.end)
            width = Dimension.value(100.dp)
            height = Dimension.value(100.dp)
        }
        constrain(greenBox) {
    
    
            top.linkTo(circle.bottom)
            bottom.linkTo(parent.bottom)
            start.linkTo(parent.start)
            end.linkTo(parent.end)
            width = Dimension.fillToConstraints
            height = Dimension.value(260.dp)
        }
        createVerticalChain(orangeBox, circle, greenBox, chainStyle = ChainStyle.SpreadInside)
    }
    ConstraintLayout(constraints, modifier = Modifier.background(Color.White).fillMaxSize()) {
    
    
        Box(
            modifier = Modifier
                .background(Color(0xFFFB8C00))
                .layoutId("orangebox")
        )
        Box(
            modifier = Modifier
                .clip(CircleShape)
                .background(Color.Blue)
                .layoutId("circle")
        )
        Box(
            modifier = Modifier
                .background(Color(0xFF2EB734))
                .layoutId("greenbox")
        )
    }
}

Fashion portrait
Landscape mode

in conclusion

Jetpack Compose ConstraintLayoutis a versatile tool for creating complex and responsive layouts. It is particularly useful when you need precise control over the relative position of components or want to improve code readability and reduce the number of lines of code by avoiding too many nested Rowssums Columns.

Guess you like

Origin blog.csdn.net/u011897062/article/details/133379386