android 可组合:不剪裁视图

wbgh16ku  于 2023-10-14  发布在  Android
关注(0)|答案(2)|浏览(136)

我有一个Row,里面包含“Spacer - Box - Spacer - Box - etc”。所有的盒子都有相同的宽度和圆角的背景。但是当没有足够的空间时,最后一个盒子会被裁剪,由于它的圆形背景,它看起来与其他盒子的宽度不同。
我尝试使用requiredWidth而不是width,但在这种情况下,框与其他框和间隔符重叠(requiredWidth使框居中)。唯一有帮助的是添加水平滚动条。但我不需要这个视图是可滚动的。有没有办法达到预期的效果?
一些图片更好地解释。
下面是width/requiredWidth的工作原理:

这就是它如何Map到我的真实的情况

b09cbbtk

b09cbbtk1#

默认可组合对象或任何未指定Modifier.clipToBounds()Modifier.clipModifier.graphicsLayer{clip}的可组合对象不会剪辑内容。你可以使用draw Modifiers在Composable之外绘制任何东西,例如当没有设置剪辑时,它也可以使用Modifier.offset或Modifier.layout等来改变位置。
所发生的情况是,可组合项是用约束和行与列使用来衡量的

var usedSpace = 0
for(measurable in measurable list) {measurable->
  measurable.measure(maxWith=constraints.maxWidth/Height - usedSpace)
  usedSpace += placeable.width/height
}

因此,当没有可用空间时,最后一个子级Composable可能会缩小到0.dp或0 px。
当你指定Modifier.requiredWidth()时,你将约束的maxWidth更改为你指定的任何值,但是当父级Composable的布局宽度与其约束宽度不匹配时,它会尝试将内容居中。你可以在this answer中找到更详细的解释。
如果不想居中,可以使用Modifier.wrapContentWidth(alignment=Start, unbounded=true)
unBounded param将约束maxWidth增加到Constraints.Infinity,内容可以用其所需的宽度进行测量,对齐设置其位置而不是中心。
你也可以像wrapContentWidth一样使用Modifier.layout,每个size Modifier都会调用measurement和layout()

我发布了两个替代方案,你可以注解掉wrapContentWidth,看看它们的行为是一样的。在布局修改器中,您可以检查哪个约束.maxWith到达下一个子级,以及它们是如何设置的。

@Preview
@Composable
private fun LayoutPlacingTest() {

    Column(
        modifier = Modifier.fillMaxSize().padding(20.dp)
    ) {
        Row(
            modifier = Modifier
                .border(4.dp, Color.Gray)
                .width(300.dp)
                .background(Color.Yellow)
        ) {
            repeat(3) { index ->
                Box(
                    modifier = Modifier
//                        .wrapContentWidth(align = Alignment.Start, unbounded = true)
                        .layout { measurable, constraints ->
                            val placeable = measurable.measure(
                                constraints.copy(maxWidth = Constraints.Infinity)
                            )

                            println("placeable: ${placeable.width}, Constraints: $constraints")

                            layout(placeable.width, placeable.height) {
                                val x = ((placeable.width - constraints.maxWidth) / 2).coerceAtLeast(0)
                                placeable.placeRelative(x, 0)
                            }
                        }
                        .size(100.dp)
                        .background(Color.Red, RoundedCornerShape(16.dp))
                )
                if (index != 2) {
                    Spacer(Modifier.width(20.dp))
                }
            }
        }
    }
}
lf5gs5x2

lf5gs5x22#

根据要求,添加一些代码,包括我根据@Megh评论的最终决定(格式有点破坏,以保持简短)。
下面是我的基本测试框:

@Composable
    private fun BoxWithWidth() {
        Box(
            modifier = Modifier
                .width(125.dp)
                .height(28.dp)
                .clip(clip)
                .background(color)
        )
    }

选项1:使用Row * 而不使用scroll*,使用Boxes * 而使用width*。
结果:最后一个视图的宽度错误

@Composable
    private fun ExampleRowWidth() {
        Row {
            BoxWithWidth()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithWidth()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithWidth()
        }
    }

选项2:使用Row * 而不使用scroll*,使用Boxes * 而不使用requiredWidth*。
结果:最后一个视图的宽度正确,但位置错误

@Composable
    private fun BoxWithRequiredWidth() {
        Box(
            modifier = Modifier
                .requiredWidth(125.dp)
                .height(28.dp)
                .clip(clip)
                .background(color)
        )
    }

    @Composable
    private fun ExampleRowRequiredWidth() {
        Row {
            BoxWithRequiredWidth()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithRequiredWidth()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithRequiredWidth()
        }
    }

选项3:使用Row * 和scroll*,Boxes * 和width*。
结果:工作正常,但现在它是可滚动的(不是我需要的,因为我在 backbone 中使用)

@Composable
    private fun ExampleRowScrollable() {
        Row(
            modifier = Modifier
                .horizontalScroll(rememberScrollState())
        ) {
            BoxWithWidth()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithWidth()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithWidth()
        }
    }

选项4:使用LazyRow(disabled scroll)和Boxes with width
结果:这就是我需要的-正确的宽度和定位没有滚动。工作良好,易于实现,但似乎仍然有点黑客。

@Composable
    private fun ExampleLazyRowNotScrollable() {
        LazyRow(
            horizontalArrangement = Arrangement.spacedBy(25.dp),
            userScrollEnabled = false
        ) {
            item { BoxWithWidth() }
            item { BoxWithWidth() }
            item { BoxWithWidth() }
        }
    }

选项5:使用不带滚动条的行 * 和带requiredWidth和wrapContentWidth* 的框 *
结果:这似乎是达到预期结果的正确方法。工作正常,没有黑客在所有。

@Composable
    private fun BoxWithRequiredWidthAndAlignment() {
        Box(
            modifier = Modifier
                .wrapContentWidth(align = Alignment.Start, unbounded = true)
                .requiredWidth(125.dp)
                .height(28.dp)
                .clip(clip)
                .background(color)
        )
    }

    @Composable
    private fun ExampleRowRequiredWidthAndAlignment() {
        Row {
            BoxWithRequiredWidthAndAlignment()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithRequiredWidthAndAlignment()
            Spacer(modifier = Modifier.width(25.dp))
            BoxWithRequiredWidthAndAlignment()
        }
    }

相关问题