ward abbass
ward abbass

Reputation: 33

Custom Layout Android Compose measurable width

I'm trying to implement a Custom "Row" Layout in Android compose like this:

@Composable
fun CustomLayout(modifier: Modifier, content: @Composable () -> Unit){
    Layout(
        modifier = modifier,
        content = content,
    ) { measurables, constraints ->
        val placeables = measurables.map { measurable ->
          measurable.measure(constraints)
        }
        var xPos = 0

        layout(constraints.maxWidth, constraints.maxHeight) {
            placeables.forEach { placeable ->
                placeable.placeRelative(x = xPos, y = 0)
                xPos += placeable.width
            }
        }
    }

}

Here is the preview for this composable:

@Preview(name = "Preview")
@Composable
fun customLayoutPreview() {
    CustomLayout(Modifier.size(1080.dp,100.dp)){
        Box(
            Modifier
                .size(32.dp, 64.dp)
                .background(Color.Red)) {

        }
        Box(
            Modifier
                .size(100.dp, 64.dp)
                .background(Color.Green)) {

        }
        Box(
            Modifier
                .size(32.dp, 64.dp)
                .background(Color.Yellow)) {

        }
    }
}

The issue here is that each measurable (Box), after being measured, got parent width, hence, 1080dp, and not the desired width passed to the boxes which caused the first child to take all space.

see: Preview

What is expected to be in theory: (Box 32dp)(Box 100dp)(Box 32dp)

What I'm missing here?

I tried the documentation, but I could not understand a way to achieve something like plain android Custom ViewGroup.onMeasure like : view.measure(MeasureSpec.makeMeasureSpec(wantedSize, AT_MOST or EXACT)), where wantedSize can be fetched from LayoutParams or from the WRAP_CONTENT strategy.

Environment: Android Studio Arctic Fox. Compose: 1.0.0. Kotlin: 1.5.10. AGP and Gradle: 7.0.0.

Upvotes: 3

Views: 2343

Answers (2)

Francesc
Francesc

Reputation: 29330

You have to make a copy of your constraints and change the min to 0. Change it like this:

@Composable
fun CustomLayout(modifier: Modifier, content: @Composable () -> Unit) {
    Layout(
        modifier = modifier,
        content = content,
    ) { measurables, constraints ->
        // MAKE A COPY WITH 0 FOR MIN
        val looseConstraints = constraints.copy(
            minWidth = 0,
            minHeight = 0
        )
        val placeables = measurables.map { measurable ->
            measurable.measure(looseConstraints)
        }
        var xPos = 0

        layout(constraints.maxWidth, constraints.maxHeight) {
            placeables.forEach { placeable ->
                placeable.placeRelative(x = xPos, y = 0)
                xPos += placeable.width
            }
        }
    }
}

enter image description here

Upvotes: 9

Richard Onslow Roper
Richard Onslow Roper

Reputation: 6863

Ok you should not use placeable.measuredWidth over there. Just replace it with placeable.width. Also, in your mapping logic, you don't at all need that val. Just do this

val placeables = measurables.map {
           it.measure(constraints)    
        }

Visit here to learn more

https://developer.android.com/codelabs/jetpack-compose-layouts?continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fcompose%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-layouts#6

Upvotes: -1

Related Questions