Thr*_*ian 13 android android-jetpack-compose
在官方文档中可以看到有一个名为 SubcomposeLayout 的布局定义为
布局的模拟,允许在测量阶段对实际内容进行细分,例如使用测量期间计算的值作为子项组成的参数。
可能的用例:
您需要知道在组合过程中父级传递的约束,并且无法仅使用自定义 Layout 或 LayoutModifier 来解决您的用例。请参阅 androidx.compose.foundation.layout.BoxWithConstraints。
您希望在构建第二个子项时使用第一个子项的大小。
您想根据可用尺寸懒惰地组合您的项目。例如,您有一个包含 100 个项目的列表,您不必组合所有项目,而是只组合当前可见的项目(例如其中 5 个),并在组件滚动时组合下一个项目。
我用SubcomposeLayout关键字搜索了 Stackoverflow,但找不到任何相关内容,创建了这个示例代码,从官方文档中复制了大部分代码,以测试和了解它是如何工作的
@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 }
Run Code Online (Sandbox Code Playgroud)
它应该根据另一个组件的尺寸重新测量一个组件,但这段代码实际上做了什么对我来说是个谜。
subcompose运作?slotId,我们可以通过某种方式获取 slotId 吗?subCompose功能说明
使用给定的 slotId 对所提供的内容执行子组合。参数: slotId - 代表我们正在组合的插槽的唯一 ID。如果您有固定的数量或槽位,您可以使用枚举作为槽位 ID,或者如果您有一个项目列表,则列表中的索引或其他一些唯一键可以工作。为了能够正确匹配重新测量之间的内容,您应该提供与上次测量期间使用的对象相同的对象。content - 定义槽的可组合内容。它可以发出多个布局,在这种情况下,返回的可测量列表将包含多个元素。
有人可以解释它的含义或/并提供工作示例吗SubcomposeLayout?
chu*_*ckj 21
它应该根据另一个组件尺寸重新测量一个组件......
SubcomposeLayout不重新测量。它允许推迟内容的组成和测量,直到知道其来自其父级的约束并且可以测量其某些内容,并且可以将其结果作为参数传递给推迟的内容。上面的示例计算 生成的内容的最大大小mainContent并将其作为参数传递给deferredContent。然后,它测量并将deferredContent两者放置在彼此之上。mainContentdeferredContent
最简单的使用示例SubcomposeLayout是 BoxWithConstraints,它只是将从其父级接收的约束直接传递到其内容。在布局期间发生的父级测量了盒子的兄弟姐妹之前,盒子的约束是未知的,因此盒子的组合content被推迟到布局。
类似地,对于上面的例子,直到layout so在layout中被调用一次之后,maxSizeofmainContent才被知道。它总是放置在 之上,因此假设以某种方式使用以避免模糊 生成的内容。可能不是可组合项的最佳设计,但可组合项的目的是说明性的,本身没有用处。deferredContentmaxSizedeferredContentmainContentdeferredContentmaxSizemainContent
请注意,subcompose可以在块中多次调用layout。例如,这就是 中发生的情况LazyRow。允许跟踪和管理通过调用创建的slotId作品。例如,如果您要从数组生成内容,您可能需要使用数组的索引,因为它允许确定在重组期间应使用上次生成的内容。并且,如果a不再被使用,则将配置其相应的组合物。SubcomposeLayoutsubcomposeslotIdSubcomposeLayoutsubcomposeslotidSubcomposeLayout
至于去哪里slotId,那取决于 的调用者SubcomposeLayout。如果内容需要它,则将其作为参数传递。上面的例子不需要它,因为slotIdfor 总是相同的deferredContent,所以它不需要去任何地方。
我根据官方文档提供的示例和@chuckj的答案制作了一个示例,但仍然不确定这种实现方法是否有效或正确。
它基本上测量最长的组件集父级宽度,并使用minimumWidthof重新测量较短的组件集Constraint,并调整较短组件的大小,如该 gif 所示。这就是 Whatsapp 缩放报价和消息长度的基本方式。
橙色和粉色容器是列,它引导 的子项DynamicWidthLayout,用于SubcomposeLayout重新测量。
@Composable
private fun DynamicWidthLayout(
modifier: Modifier = Modifier,
mainContent: @Composable () -> Unit,
dependentContent: @Composable (IntSize) -> Unit
) {
SubcomposeLayout(modifier = modifier) { constraints ->
var mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent).map {
it.measure(constraints)
}
var maxSize =
mainPlaceables.fold(IntSize.Zero) { currentMax: IntSize, placeable: Placeable ->
IntSize(
width = maxOf(currentMax.width, placeable.width),
height = maxOf(currentMax.height, placeable.height)
)
}
val dependentMeasurables: List<Measurable> = subcompose(SlotsEnum.Dependent) {
// Send maxSize of mainComponent to
// dependent composable in case it might be used
dependentContent(maxSize)
}
val dependentPlaceables: List<Placeable> = dependentMeasurables
.map { measurable: Measurable ->
measurable.measure(Constraints(maxSize.width, constraints.maxWidth))
}
// Get maximum width of dependent composable
val maxWidth = dependentPlaceables.maxOf { it.width }
println(" DynamicWidthLayout-> maxSize width: ${maxSize.width}, height: ${maxSize.height}")
// If width of dependent composable is longer than main one, remeasure main one
// with dependent composable's width using it as minimumWidthConstraint
if (maxWidth > maxSize.width) {
println(" DynamicWidthLayout REMEASURE MAIN COMPONENT")
// !!! CANNOT use SlotsEnum.Main here why?
mainPlaceables = subcompose(2, mainContent).map {
it.measure(Constraints(maxWidth, constraints.maxWidth))
}
}
// Our final maxSize is longest width and total height of main and dependent composables
maxSize = IntSize(
maxSize.width.coerceAtLeast(maxWidth),
maxSize.height + dependentPlaceables.maxOf { it.height }
)
layout(maxSize.width, maxSize.height) {
// Place layouts
mainPlaceables.forEach { it.placeRelative(0, 0) }
dependentPlaceables.forEach {
it.placeRelative(0, mainPlaceables.maxOf { it.height })
}
}
}
}
enum class SlotsEnum { Main, Dependent }
Run Code Online (Sandbox Code Playgroud)
用法
@Composable
private fun TutorialContent() {
val density = LocalDensity.current.density
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
var mainText by remember { mutableStateOf(TextFieldValue("Main Component")) }
var dependentText by remember { mutableStateOf(TextFieldValue("Dependent Component")) }
OutlinedTextField(
modifier = Modifier
.padding(horizontal = 8.dp)
.fillMaxWidth(),
value = mainText,
label = { Text("Main") },
placeholder = { Text("Set text to change main width") },
onValueChange = { newValue: TextFieldValue ->
mainText = newValue
}
)
OutlinedTextField(
modifier = Modifier
.padding(horizontal = 8.dp)
.fillMaxWidth(),
value = dependentText,
label = { Text("Dependent") },
placeholder = { Text("Set text to change dependent width") },
onValueChange = { newValue ->
dependentText = newValue
}
)
DynamicWidthLayout(
modifier = Modifier
.padding(8.dp)
.background(Color.LightGray)
.padding(8.dp),
mainContent = {
println(" DynamicWidthLayout-> MainContent {} composed")
Column(
modifier = Modifier
.background(orange400)
.padding(4.dp)
) {
Text(
text = mainText.text,
modifier = Modifier
.background(blue400)
.height(40.dp),
color = Color.White
)
}
},
dependentContent = { size: IntSize ->
// Measure max width of main component in dp retrieved
// by subCompose of dependent component from IntSize
val maxWidth = with(density) {
size.width / this
}.dp
println(
" DynamicWidthLayout-> DependentContent composed " +
"Dependent size: $size, "
+ "maxWidth: $maxWidth"
)
Column(
modifier = Modifier
.background(pink400)
.padding(4.dp)
) {
Text(
text = dependentText.text,
modifier = Modifier
.background(green400),
color = Color.White
)
}
}
)
}
}
Run Code Online (Sandbox Code Playgroud)
完整的源代码在这里。
| 归档时间: |
|
| 查看次数: |
15329 次 |
| 最近记录: |