首页 > 解决方案 > Jetpack Compose SubcomposeLayout 是如何工作的?

问题描述

从官方文档中可以看出,有一个名为 SubcomposeLayout 的布局定义为

布局的模拟,允许在测量阶段对实际内容进行子组合,例如使用测量期间计算的值作为子项组合的参数。

可能的用例:

您需要知道父级在组合过程中传递的约束,并且不能仅使用自定义 Layout 或 LayoutModifier 来解决您的用例。请参阅 androidx.compose.foundation.layout.BoxWithConstraints。

您想在第二个孩子的构图中使用一个孩子的大小。

您想根据可用大小懒惰地组合您的项目。例如,您有一个包含 100 个项目的列表,而不是编写所有项目,您只编写当前可见的项目(比如其中 5 个)并在组件滚动时编写下一个项目。

我用关键字搜索了 StackoverflowSubcomposeLayout但找不到任何关于它的信息,创建了这个示例代码,从官方文档中复制了大部分内容,以测试并了解它是如何工作的

@Composable
private fun SampleContent() {

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
    ) {
        SubComponent(
            mainContent = {
                Text(
                    "MainContent",
                    modifier = Modifier
                        .background(Color(0xffF44336))
                        .height(60.dp),
                    color = Color.White
                )
            },
            dependentContent = {
                val size = it

                println(" Dependent size: $size")
                Column() {

                    Text(
                        "Dependent Content",
                        modifier = Modifier
                            .background(Color(0xff9C27B0)),
                        color = Color.White
                    )
                }
            }
        )

    }
}

@Composable
private fun SubComponent(
    mainContent: @Composable () -> Unit,
    dependentContent: @Composable (IntSize) -> Unit
) {

    SubcomposeLayout { constraints ->

        val mainPlaceables = subcompose(SlotsEnum.Main, mainContent).map {
            it.measure(constraints)

        }

        val maxSize = mainPlaceables.fold(IntSize.Zero) { currentMax, placeable ->
            IntSize(
                width = maxOf(currentMax.width, placeable.width),
                height = maxOf(currentMax.height, placeable.height)
            )
        }

        layout(maxSize.width, maxSize.height) {

            mainPlaceables.forEach { it.placeRelative(0, 0) }

            subcompose(SlotsEnum.Dependent) {
                dependentContent(maxSize)
            }.forEach {
                it.measure(constraints).placeRelative(0, 0)
            }

        }
    }
}

enum class SlotsEnum { Main, Dependent }

它应该根据另一个组件的大小重新测量一个组件,但这段代码的实际作用对我来说是个谜。

  1. 函数是如何subcompose工作的?
  2. 有什么意义,slotId我们可以通过某种方式获得 slotId 吗?

subCompose功能说明

使用给定的 slotId 执行提供的内容的子组合。参数: slotId - 代表我们正在组合的插槽的唯一 ID。如果您有固定数量或插槽,则可以使用枚举作为插槽 id,或者如果您有项目列表,则列表中的索引或其他一些唯一键可以工作。为了能够在重新测量之间正确匹配内容,您应该提供与您在上次测量期间使用的对象相同的对象。content - 定义插槽的可组合内容。它可以发出多个布局,在这种情况下,返回的 Measurables 列表将具有多个元素。

有人可以解释它的含义或/并提供一个工作样本SubcomposeLayout吗?

标签: androidandroid-jetpack-compose

解决方案


它应该根据另一个组件尺寸重新测量一个组件...

SubcomposeLayout不重新测量。它允许推迟内容的组成和测量,直到其父项的约束已知并且可以测量其内容,从中可以将结果作为参数传递给延迟的内容。上面的例子计算生成的内容的最大大小,mainContent并将其作为参数传递给deferredContent. 然后,它测量deferredContent并将两者mainContent放在deferredContent一起。

最简单的使用示例SubcomposeLayoutBoxWithConstraints,它只是将其从父级接收到的约束直接传递给其内容。在布局期间发生的父级测量框的兄弟姐妹之前,框的约束是未知的,因此组合content被推迟到布局。

类似地,对于上面的示例,直到 layout 才知道 of,所以maxSize在layout once被计算时调用。它总是放在最上面,因此假定它以某种方式使用以避免模糊由. 可能不是可组合的最佳设计,但可组合的目的是说明性的,本身没有用处。mainContentdeferredContentmaxSizedeferredContentmainContentdeferredContentmaxSizemainContent

请注意,subcompose可以在块中多次调用layout。例如,这是发生在LazyRow. slotId允许SubcomposeLayout跟踪和管理调用创建的作品subcompose。例如,如果您从一个数组生成内容,您可能希望使用数组的索引作为它slotId允许SubcomposeLayout确定subcompose在重组期间应该使用上次生成的内容。此外,如果 aslotid不再使用,SubcomposeLayout将处理其相应的组合。

至于去哪里slotId,这取决于调用者SubcomposeLayout。如果内容需要它,请将其作为参数传递。上面的例子不需要它,因为slotId它总是一样的,deferredContent所以它不需要去任何地方。


推荐阅读