kotlin Compose:基于measuredWidth在Layout中放置可测量对象

waxmsbnn  于 2022-11-16  发布在  Kotlin
关注(0)|答案(1)|浏览(130)

我 试图 实现 一 个 SegmentedControl 可 组合 函数 , 但是 如果 其中 一 个 段 需要 更多 的 空间 , 则 允许 段 具有 不同 的 大小 。 到 目前 为止 , 我 已经 实现 了 基本 的 实现 , 其中 所有 段 的 宽度 都 是 相等 的 :

但是 , 正如 您 所 看到 的 , FooBar 段 可以 轻松 地 占用 较少 的 空间 , 以便 为 Some very long string 腾出 空间 。
所以 我 的 要求 是 :

  • 当 每个 子 项 的 所 需 宽度 之 和 小于 传入 约束 的 宽度 时 , 均匀 分布 子 项
  • 否则 , 收缩 可以 收缩 的 子 项 , 直到 所有 子 项 都 可见
  • 如果 不 可能 , 请 找到 一 个 可以 显示 最 大 内容 量 的 配置 。

在 尝试 实现 第 一 个 要求 时 , 我 很快 想起 , 使用 默认 的 Layout 组合 是 不 可能 的 , 因为 每个 布局 通道 只 允许 一 个 测量 , 这 是 有 充分 理由 的 。

Layout(
    content = {
        // Segments
    }
) { segmentsMeasurables, constraints ->
    var placeables = segmentsMeasurables.map {
        it.measure(constraints)
    }
    // In case every placeable has enough space in the layout,
    // we divide the space evenly between them
    if (placeables.sumOf { it.measuredWidth } <= constraints.maxWidth) {
        placeables = segmentsMeasurables.map { 
            it.measure( // <- NOT ALLOWED!
                Constraints.fixed(
                    width = constraints.maxWidth / state.segmentCount,
                    height = placeables[0].height
                )
            )
        }
    }

    layout(
        width = placeables.sumOf { it.width },
        height = placeables[0].height
    ) {
        var xOffset = 0
        placeables.forEachIndexed { index, placeable ->
            xOffset += placeables.getOrNull(index - 1)?.width ?: 0
            placeable.placeRelative(
                x = xOffset,
                y = 0
            )
        }
    }
}

中 的 每 一 个
我 还 研究 了 SubcomposeLayout , 但 它 似乎 没有 做 我 需要 的 事情 ( 我 的 用例 不 需要 子 组合 ) 。
我 可以 想象 一 个 笨拙 的 解决 方案 , 其中 我 强制 至少 两 个 布局 通道 来 收集 孩子 的 大小 , 然后 才 执行 布局 逻辑 , 但 它 将 是 不 稳定 的 , 没有 性能 , 并 将 产生 一 个 框架 与 布局 不良 的 孩子 。
那 怎么 做 才 合适 呢 ? 我 错过 什么 了 吗 ?

hwamh0ep

hwamh0ep1#

你必须使用内在的度量

@Composable
fun Tiles(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit,
) {
    Layout(
        modifier = modifier,
        content = content,
    ) { measurables, constraints ->
        val widths = measurables.map { measurable -> measurable.maxIntrinsicWidth(constraints.maxHeight) }
        val totalWidth = widths.sum()
        val placeables: List<Placeable>
        if (totalWidth > constraints.maxWidth) {
            // do not fit, set all to same width
            val width = constraints.maxWidth / measurables.size
            val itemConstraints = constraints.copy(
                minWidth = width,
                maxWidth = width,
            )
            placeables = measurables.map { measurable -> measurable.measure(itemConstraints) }
        } else {
            // set each to its required width, and split the remainder evenly
            val remainder = (constraints.maxWidth - totalWidth) / measurables.size
            placeables = measurables.mapIndexed { index, measurable ->
                val width = widths[index] + remainder
                measurable.measure(
                    constraints = constraints.copy(
                        minWidth = width,
                        maxWidth = width,
                    )
                )
            }
        }
        layout(
            width = constraints.maxWidth,
            height = constraints.maxHeight,
        ) {
            var x = 0
            placeables.forEach { placeable ->
                placeable.placeRelative(
                    x = x,
                    y = 0
                )
                x += placeable.width
            }
        }
    }
}

@Preview(widthDp = 360)
@Composable
fun PreviewTiles() {
    PlaygroundTheme {
        Surface(
            color = MaterialTheme.colorScheme.background
        ) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(all = 16.dp),
            ) {
                Tiles(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(40.dp)
                ) {
                    Text(
                        text = "Foo",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Red.copy(alpha = .3f))
                    )
                    Text(
                        text = "Bar",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Blue.copy(alpha = .3f))
                    )
                }
                Tiles(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 16.dp)
                        .height(40.dp)
                ) {
                    Text(
                        text = "Foo",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Red.copy(alpha = .3f))
                    )
                    Text(
                        text = "Bar",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Blue.copy(alpha = .3f))
                    )
                    Text(
                        text = "Some very long text",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Red.copy(alpha = .3f))
                    )
                }
                Tiles(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 16.dp)
                        .height(40.dp)
                ) {
                    Text(
                        text = "Foo",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Red.copy(alpha = .3f))
                    )
                    Text(
                        text = "Bar",
                        textAlign = TextAlign.Center,
                        modifier = Modifier.background(Color.Blue.copy(alpha = .3f))
                    )
                    Text(
                        text = "Some even much longer text that doesn't fit",
                        textAlign = TextAlign.Center,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                        modifier = Modifier.background(
                            Color.Red.copy(alpha = .3f)
                        )
                    )
                }
            }
        }
    }
}

相关问题