Jetpack Compose自定义Layout宽高修饰符作用于子元素的原因及解决方法
问题成因
- 测量子元素时直接透传了父布局收到的
constraints参数:当给自定义Layout的modifier设置宽高属性后,constraints会携带对应的尺寸限制规则,子元素测量时会强制遵守该限制,因此会被拉伸到匹配父布局的尺寸。 - 原有代码硬编码使用屏幕宽度计算父布局自身尺寸,完全忽略了
constraints的限制,会导致给modifier设置的宽高属性不生效,不符合Compose布局的开发约定。
修复方案
只需要调整两处逻辑即可解决问题:
- 测量子元素时传入无上限的默认约束,让子元素仅根据自身内容计算尺寸,不受父布局的尺寸限制
- 父布局最终确定自身尺寸时,先通过传入的
constraints做校验,适配通过modifier设置的宽高规则
修改后的完整代码如下:
@Composable fun CircularRevealMenu( modifier: Modifier, contentPadding: Dp = 16.dp, circleRadius: () -> Float, content: @Composable () -> Unit ) { val configuration = LocalConfiguration.current Layout(content = content, modifier = modifier) { children, constraints -> val screenWidth = configuration.screenWidthDp.dp.toPx() * circleRadius() // 子元素使用无约束测量,不受父布局尺寸限制 val placeables = children.map { it.measure(Constraints()) } val maxItemHeight = placeables.maxOf { it.height } val maxItemWidth = placeables.maxOf { it.width } val gap = 90 / placeables.size val radiusOffset = (max(maxItemHeight, maxItemWidth) / 2) + contentPadding.toPx() val radius = screenWidth - radiusOffset val offset = 180 - gap / 2f // 父布局尺寸先适配传入的约束,保证modifier设置的宽高生效 val layoutWidth = constraints.constrainWidth(screenWidth.toInt()) val layoutHeight = constraints.constrainHeight(screenWidth.toInt()) layout(layoutWidth, layoutHeight) { for (i in placeables.indices) { val radians = Math.toRadians((offset - (gap * i)).toDouble()) placeables[i].placeRelative( x = (cos(radians) * radius + screenWidth).toInt() - placeables[i].width / 2, y = (sin(radians) * radius + 0).toInt() - placeables[i].height / 2 ) } } } } enum class CircularMenuStates { Collapsed, Expanded } @Preview(showBackground = true) @Composable fun PreviewCircularMenu() { CircularRevealMenu(modifier = Modifier.fillMaxWidth(), circleRadius = { 1f }) { Text(text = "Item 1") Text(text = "Item 2") Text(text = "Item 3") Text(text = "Item 4") } }
内容的提问来源于stack exchange,提问作者Jaseem
相关产品推荐
相关产品推荐

