简体   繁体   English

如何在 Jetpack Compose 中实现这种布局

[英]How to achieve this layout in Jetpack Compose

I'm trying to use the new Jetpack Compose UI framework, but I'm running into an issue.我正在尝试使用新的 Jetpack Compose UI 框架,但遇到了问题。 I'd like to achieve this layout, which in xml is pretty easy to achieve:我想实现这种布局,在 xml 中很容易实现:

正确的布局

But I can't figure out how to make the vertical divider take up the available vertical space, without specifying a fixed height.但我不知道如何让垂直分隔线占用可用的垂直空间,而不指定固定高度。 This code that I've tried doesn't seem to work:我尝试过的这段代码似乎不起作用:

@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))
        }
    }
}

I keep getting this result:我不断得到这个结果:

在此处输入图像描述

I also tried with ConstraintLayout, but it still didn't work我也尝试过使用 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
            )
        }
    }
}

But nothing seems to work.但似乎没有任何效果。 Is this a bug, a missing feature or am I just missing something?这是一个错误,一个缺失的功能还是我只是错过了什么?

EDIT: Apparently the real problem is that the divider doesn't know how to measure when the Surface doesn't have a fixed height, setting height equal to some number solves the issue, but then the view doesn't adapt to the content height anymore, so this can't be the solution编辑:显然,真正的问题是当 Surface 没有固定高度时分隔器不知道如何测量,将高度设置为某个数字可以解决问题,但是视图不适应内容高度不再,所以这不是解决方案

You can set Intrinsic.Max for the preferredHeight of the Row , then set the Spacer to fill max height.您可以为RowpreferredHeight设置Intrinsic.Max ,然后将Spacer设置为填充最大高度。 You can read more on Intrinsic s in this codelab section .您可以在此 codelab 部分中阅读有关Intrinsic的更多信息。

@Composable
fun ListItem() {
    Surface(
        modifier = Modifier.fillMaxWidth(),
        shape = RoundedCornerShape(8.dp),
        elevation = 2.dp
    ) {
        Row(
            modifier = Modifier.fillMaxWidth().preferredHeight(IntrinsicSize.Max),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Column(
                modifier = Modifier
                    .padding(8.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = "Code")
                Text(text = "2456")
            }
            Spacer(
                modifier = Modifier
                    .preferredWidth(1.dp)
                    .fillMaxHeight()
                    .background(color = Color.Black.copy(0.12f))
            )
            Spacer(modifier = Modifier.weight(1f))
            Text(
                modifier = Modifier
                    .padding(horizontal = 8.dp, vertical = 34.dp),
                text = "Some name"
            )
            Spacer(modifier = Modifier.weight(1f))
        }
    }
}

With 1.0.0-beta02 you can apply:使用1.0.0-beta02您可以申请:

  • the modifier .height(IntrinsicSize.Max) to the Row修改器.height(IntrinsicSize.Max)Row
  • the modifiers .width(1.dp).fillMaxHeight() to the Spacer修饰符.width(1.dp).fillMaxHeight()Spacer

You can read more about the Intrinsic measurements here .您可以在此处阅读有关内在测量的更多信息。

Something like:就像是:

Row(
    modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
    verticalAlignment = Alignment.CenterVertically
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        ....
    ) {
        Text(text = "....")
    }
    Spacer(
        modifier = Modifier
            .width(1.dp)
            .fillMaxHeight()
            .background(color = MaterialTheme.colors.onSurface.copy(0.12f))
    )
    Text(...)
}

在此处输入图片说明

I've solved it using constraint layout:我已经使用约束布局解决了它:

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)
            )
        }
    }
}

The key part is using that modifier height = Dimension.fillToConstraints关键部分是使用修饰符height = Dimension.fillToConstraints

There are plenty of solutions here, but I thought I could demonstrate the ConstraintLayout approach and add a helpful usage of the IntrinsicSize enum that solves one of the issues (needing an adaptive height for the composable).这里有很多解决方案,但我想我可以演示 ConstraintLayout 方法并添加IntrinsicSize枚举的有用用法,以解决其中一个问题(需要一个可组合的自适应高度)。 Interestingly, either IntrinsicSize.Max or IntrinsicSize.Min will yield the desired behavior.有趣的是, IntrinsicSize.MaxIntrinsicSize.Min将产生所需的行为。

I used most of your code.我使用了你的大部分代码。 The key differences are:主要区别是:

  • declares a guideline (my value passed in for the fraction does not produce the exact result you were looking for, but can be adjusted easily (use a fraction slightly smaller than .2) This can be useful if you expect wrapContent to alter your left Text to vary the location of a spacer, but would prefer a consistent spacer location across a list of these items.声明一个准则(我为分数传入的值不会产生您正在寻找的确切结果,但可以轻松调整(使用略小于 .2 的分数)如果您希望 wrapContent 更改您的左文本,这可能很有用改变间隔的位置,但希望在这些项目的列表中保持一致的间隔位置。
  • others have mentioned, spacer modifier should include .fillMaxHeight()其他人已经提到,间隔修饰符应该包括.fillMaxHeight()
  • define the height of the surface wrapper to be .height(IntrinsicSize.Min) docs ref here: https://developer.android.com/jetpack/compose/layout#intrinsic-measurements将表面包装器的高度定义为.height(IntrinsicSize.Min)文档参考: https : //developer.android.com/jetpack/compose/layout#intrinsic-measurements
  • divider start is constrained to the guideline分隔线开始受限于指南
  • had to change the Spacer modifier to access the width , instead of preferredWidth必须更改 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
            )
        }
    }
}

I think Row layout is enough.我认为行布局就足够了。

@Preview(showBackground = true, heightDp = 100)
@Composable
fun ListItem(item: PlateUI.Plate = PlateUI.Plate()) {
    Card(
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(
            modifier = Modifier
                .fillMaxSize(),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Text(
                modifier = Modifier.padding(8.dp),
                text = "Code\n${item.code}",
                textAlign = TextAlign.Center
            )
            Box(
                Modifier
                    .fillMaxHeight()
                    .width(1.dp)
                    .background(color = MaterialTheme.colors.onSurface.copy(0.12f))
            )
            Text(
                modifier = Modifier
                    .weight(1f)
                    .padding(8.dp),
                text = item.name,
                textAlign = TextAlign.Center
            )
        }
    }
}

列表项预览

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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