简体   繁体   中英

LazyHorizontalGrid inside LazyColumn

I have a LazyColumn and inside it I want to display a horizontal row with two columns, so I was trying LazyHorizontalGrid to achieve it. But my application crashes with the exception - IllegalArgumentException: LazyHorizontalGrid's height should be bound by parent . Below is my code what I am using, can anyone please help to fix it or any other way through which I can make a row have two columns.

@Composable
fun HomeItem1() {
    Surface(modifier = Modifier.nestedScroll(rememberViewInteropNestedScrollConnection())) {
        LazyColumn {
            //other contents
            item {
                LazyHorizontalGrid(
                    rows = GridCells.Fixed(3),
                    horizontalArrangement = Arrangement.spacedBy(16.dp),
                    verticalArrangement = Arrangement.spacedBy(16.dp)
                ) {
                    items(arrayList.size) {
                        Text(arrayList[it])
                    }
                }
            }
        }
    }
}

All you need to calculate the Grid's height beforehand.

    @Composable
    fun HomeItem1() {
        Surface(modifier = Modifier.nestedScroll(rememberViewInteropNestedScrollConnection())) {
        LazyColumn {
            //other contents
            item {
                LazyHorizontalGrid(
                    modifier = Modifier.height(176.dp), // itemHeight * rowCount + verticalSpacing * (rowCount - 1)
                    rows = GridCells.Fixed(3),
                    horizontalArrangement = Arrangement.spacedBy(16.dp),
                    verticalArrangement = Arrangement.spacedBy(16.dp)
                ) {
                    items(arrayList.size) {
                        Text(arrayList[it], modifier = Modifier.height(48.dp))
                    }
                }
            }
        }
    }
}

LazyList exceptions with dimensions occur when you try to measure respective dimension with Constraints.Infinity . I explained about in this answer how Constraints are applied based on modifiers, Modifier.scroll() . Also, using a scrollable parent Composable like LazyColumn creates Constraints with infinite height , LazyRow creates Constraints with infinite width .

You can use Modifier.height(maxHeight) with maxHeight from

You can use Modifier.height(maxHeight) or Modifier.layout{} and constrain max height to screen height or selected height in px. Constraints return dimensions in px that is to be used with layout function. Getting Constrains height from parent by wrapping it with BoxWithConstraints returns non-infinite height

@Composable
private fun Sample() {

    val arrayList = remember {
        mutableStateListOf<String>().apply {
            repeat(40) {
                add("Item $it")
            }
        }
    }


    Column(
        modifier = Modifier
            .border(4.dp, Color.Green)
            .fillMaxSize()

    ) {

        BoxWithConstraints {

            val parentConstraints = this.constraints
            val maxHeight = this.maxHeight

            LazyColumn {
                //other contents
                item {
                    Image(
                        modifier = Modifier
                            .height(200.dp)
                            .fillMaxWidth(),
                        painter = painterResource(id = R.drawable.landscape2),
                        contentDescription = null,
                        contentScale = ContentScale.FillBounds
                    )
                }
                item {

                    LazyHorizontalGrid(
                        modifier = Modifier
                            .border(2.dp, Color.Red)
                           // Alternative 1
                           // .height(maxHeight)
                           // Alternative 2
                            .layout { measurable, constraints ->
                                val placeable = measurable.measure(
                                    constraints.copy(maxHeight = parentConstraints.maxHeight)
                                )

                                layout(placeable.width, placeable.height) {
                                    placeable.placeRelative(0, 0)
                                }
                            },
                        rows = GridCells.Fixed(3),
                        horizontalArrangement = Arrangement.spacedBy(16.dp),
                        verticalArrangement = Arrangement.spacedBy(16.dp)
                    ) {
                        items(arrayList.size) {
                            Box(
                                modifier = Modifier
                                    .shadow(2.dp, RoundedCornerShape(8.dp))
                                    .background(Color.White)
                                    .aspectRatio(1f)
                                    .padding(2.dp)
                            ) {
                                Text(arrayList[it])
                            }
                        }
                    }
                }
            }
        }
    }
}

The example above creates a LazyHorizontalGrid with full height of screen, i added an Image as another element to show that grid covers parent height.

The important thing here is how you set maxHeight for Constraints in

constraints.copy(maxHeight = parentConstraints.maxHeight)

If you want grid to match item size then you should measure size of an item

using rowCount * itemHeight + (rowCount-1)* verticalSpacingInPx .

constraints.copy(maxHeight = 600f) for instance.

If you don't know the height of the item you should use Modifier.onSizeChanged or as in this answer SubcomposeLayout or Layout

How to get exact size without recomposition?

And using Text as dependent item we can do it as

@Composable
private fun Sample2() {

    val arrayList = remember {
        mutableStateListOf<String>().apply {
            repeat(40) {
                add("Item $it")
            }
        }
    }

    val mainComposable = @Composable {
        Box(
            modifier = Modifier
                .shadow(2.dp, RoundedCornerShape(8.dp))
                .background(Color.White)
                .size(80.dp)
                .padding(2.dp)
        ) {
            Text(arrayList[0])
        }
    }

    Column(
        modifier = Modifier
            .border(4.dp, Color.Green)
            .fillMaxSize()

    ) {

        DimensionSubcomposeLayout(
            rowCount = 3,
            verticalSpacing = 16.dp,
            mainContent = {
                mainComposable()
            }
        ) { size: Dp ->
            LazyColumn {

                //other contents
                item {
                    Image(
                        modifier = Modifier
                            .height(200.dp)
                            .fillMaxWidth(),
                        painter = painterResource(id = R.drawable.landscape2),
                        contentDescription = null,
                        contentScale = ContentScale.FillBounds
                    )
                }

                item {
                    LazyHorizontalGrid(
                        modifier = Modifier
                            .height(size)
                            .border(2.dp, Color.Red),
                        rows = GridCells.Fixed(3),
                        horizontalArrangement = Arrangement.spacedBy(16.dp),
                        verticalArrangement = Arrangement.spacedBy(16.dp)
                    ) {
                        items(arrayList.size) {
                            Box(
                                modifier = Modifier
                                    .shadow(2.dp, RoundedCornerShape(8.dp))
                                    .background(Color.White)
                                    .size(80.dp)
                                    .padding(2.dp)
                            ) {
                                Text(arrayList[it])
                            }
                        }
                    }
                }
            }
        }
    }
}

SubComposeLayout that uses Text height and spacing to calculate height for LazyHorizontal Grid

@Composable
fun DimensionSubcomposeLayout(
    modifier: Modifier = Modifier,
    rowCount: Int,
    verticalSpacing: Dp = 0.dp,
    mainContent: @Composable () -> Unit,
    dependentContent: @Composable (Dp) -> Unit
) {

    val density = LocalDensity.current
    val verticalSpacingPx = density.run { verticalSpacing.toPx() }

    SubcomposeLayout(
        modifier = modifier
    ) { constraints: Constraints ->

        // Subcompose(compose only a section) main content and get Placeable
        val mainPlaceable = subcompose(SlotsEnum.Main, mainContent)
            .map {
                it.measure(constraints)
            }
            .first()


        // Get max width and height of main component

        val maxHeight = mainPlaceable.height * rowCount + (rowCount - 1) * verticalSpacingPx

        val dpSize = with(density.density) {
            maxHeight.toDp()
        }

        val dependentPlaceable = subcompose(SlotsEnum.Dependent) {
            dependentContent(dpSize)
        }
            .map { measurable: Measurable ->
                measurable.measure(constraints)
            }
            .first()

        layout(dependentPlaceable.width, dependentPlaceable.height) {
            dependentPlaceable.placeRelative(0, 0)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM