如何在Jetpack Compose中实现此布局

时间:2020-09-24 09:39:35

标签: android android-layout android-jetpack-compose

我正在尝试使用新的Jetpack Compose UI框架,但是遇到了问题。我想实现这种布局,以xml格式实现非常容易:

Correct layout

但是我不知道如何在不指定固定高度的情况下使垂直分隔器占用可用的垂直空间。我尝试过的这段代码似乎无效:

@Composable
fun ListItem(item: PlateUI.Plate) {
    Surface(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(8.dp),
        elevation = 2.dp
    ) {
        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Column(
                modifier = Modifier
                    .padding(8.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "Code")
                Text(text = item.code)
            }
            Spacer(
                modifier = Modifier
                    .preferredWidth(1.dp)
                    .background(color = MaterialTheme.colors.onSurface.copy(0.12f))
            )
            Spacer(modifier = Modifier.weight(1f))
            Text(
                modifier = Modifier
                    .padding(horizontal = 8.dp, vertical = 34.dp),
                text = item.name
            )
            Spacer(modifier = Modifier.weight(1f))
        }
    }
}

我一直得到这个结果:

enter image description here

我也尝试过ConstraintLayout,但仍然无法正常工作

@Composable
fun ListItem(item: PlateUI.Plate) {
    Surface(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(8.dp),
        elevation = 2.dp
    ) {
        ConstraintLayout(
            modifier = Modifier.fillMaxWidth(),
        ) {
            val(column, divider, text) = createRefs()
            Column(
                modifier = Modifier
                    .padding(8.dp)
                    .constrainAs(column){
                        top.linkTo(parent.top)
                        bottom.linkTo(parent.bottom)
                        start.linkTo(parent.start)
                    },
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "Code")
                Text(text = item.code)
            }
            Spacer(
                modifier = Modifier
                    .preferredWidth(1.dp)
                    .background(color = MaterialTheme.colors.onSurface.copy(0.12f))
                    .constrainAs(divider){
                        top.linkTo(parent.top)
                        bottom.linkTo(parent.bottom)
                        start.linkTo(column.end)
                    }
            )
            Text(
                modifier = Modifier
                    .padding(horizontal = 8.dp, vertical = 34.dp)
                    .constrainAs(text){
                        start.linkTo(divider.end)
                        end.linkTo(parent.end)
                        top.linkTo(parent.top)
                        bottom.linkTo(parent.bottom)
                    },
                text = item.name
            )
        }
    }
}

但是似乎没有任何效果。这是错误,缺少的功能还是我只是缺少什么?

编辑:显然,真正的问题是分频器不知道如何在Surface不具有固定高度的情况下进行测量,将高度设置为等于某个数字可以解决此问题,但随后视图就无法适应内容的高度了,所以这不是解决方案

4 个答案:

答案 0 :(得分:3)

您可以为Intrinsic.Max的{​​{1}}设置preferredHeight,然后将Row设置为填充最大高度。您可以在 this codelab section 中阅读有关 Spacer 的更多信息。

Intrinsic

答案 1 :(得分:1)

您可以使用Modifier.fillMaxHeight()

类似的东西:

    Spacer(
            modifier = Modifier
                    .preferredWidth(1.dp)
                    .fillMaxHeight()
                    .background(color = MaterialTheme.colors.onSurface.copy(0.12f))
    )

enter image description here

答案 2 :(得分:1)

我已经使用约束布局解决了这个问题:

Box(modifier = Modifier.padding(Dp(50f))) {
    ConstraintLayout(
        modifier = Modifier
            .border(width = Dp(1f), color = Color.Black)
            .fillMaxWidth()
    ) {
        val (left, divider, right) = createRefs()
        Column(
            modifier = Modifier
                .padding(horizontal = Dp(20f))
                .constrainAs(left) {
                    width = Dimension.wrapContent
                    start.linkTo(parent.start)
                    top.linkTo(parent.top)
                    end.linkTo(divider.start)
                    bottom.linkTo(parent.bottom)
                }
        ) {
            Text(text = "Code")
            Text(text = "A12")
        }
        Box(
            modifier = Modifier
                .width(Dp(1f))
                .background(Color.Black)
                .constrainAs(divider) {
                    width = Dimension.wrapContent
                    height = Dimension.fillToConstraints
                    start.linkTo(left.end)
                    top.linkTo(parent.top)
                    end.linkTo(right.start)
                    bottom.linkTo(parent.bottom)
                }
        )
        Box(
            modifier = Modifier
                .constrainAs(right) {
                    width = Dimension.fillToConstraints
                    start.linkTo(divider.end)
                    top.linkTo(parent.top)
                    end.linkTo(parent.end)
                    bottom.linkTo(parent.bottom)
                }
        ) {
            Text(
                text = "Test",
                modifier = Modifier
                    .padding(vertical = Dp(100f))
                    .align(Alignment.Center)
            )
        }
    }
}

关键部分是使用修饰符 height = Dimension.fillToConstraints

答案 3 :(得分:0)

这里有很多解决方案,但我想我可以演示 ConstraintLayout 方法并添加 IntrinsicSize 枚举的有用用法来解决其中一个问题(需要一个可组合的自适应高度)。有趣的是,IntrinsicSize.MaxIntrinsicSize.Min 都会产生所需的行为。

我使用了你的大部分代码。主要区别在于:

  • 声明一个准则(我为分数传入的值不会产生您正在寻找的确切结果,但可以轻松调整(使用略小于 .2 的分数)如果您希望 wrapContent 改变,这可能很有用您的左侧文本以改变分隔符的位置,但希望在这些项目的列表中使用一致的分隔符位置。
  • 其他人已经提到,间隔修饰符应该包括 .fillMaxHeight()
  • 将表面包装器的高度定义为 .height(IntrinsicSize.Min) 文档参考:https://developer.android.com/jetpack/compose/layout#intrinsic-measurements
  • divider start 受限于指南
  • 必须更改 Spacer 修饰符才能访问 width,而不是 preferredWidth
@Composable
fun ListItem(item: Plate) {
    Surface(
        modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Min),
        shape = RoundedCornerShape(8.dp),
        elevation = 2.dp
    ) {
        ConstraintLayout(
            modifier = Modifier.fillMaxWidth(),
        ) {
            val guideline = createGuidelineFromStart(0.2f)

            val(column, divider, text) = createRefs()

            Column(
                modifier = Modifier
                    .padding(8.dp)
                    .constrainAs(column){
                        top.linkTo(parent.top)
                        bottom.linkTo(parent.bottom)
                        start.linkTo(parent.start)
                        end.linkTo(guideline)
                    },
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "Code")
                Text(text = item.code)
            }
            Spacer(
                modifier = Modifier
                    .constrainAs(divider){
                        top.linkTo(column.top)
                        bottom.linkTo(column.bottom)
                        start.linkTo(guideline)
                    }
                    .width(1.dp)
                    .fillMaxHeight()
                    .background(color = MaterialTheme.colors.onSurface.copy(0.12f))
            )
            Text(
                modifier = Modifier
                    .padding(horizontal = 8.dp, vertical = 34.dp)
                    .constrainAs(text){
                        start.linkTo(divider.end)
                        end.linkTo(parent.end)
                        top.linkTo(parent.top)
                        bottom.linkTo(parent.bottom)
                    },
                text = item.name
            )
        }
    }
}