Jetpack Compose中如何获取间接后代组件的尺寸与位置?
Jetpack Compose中如何获取间接后代组件的尺寸与位置?
嗨,这个需求我之前也碰到过,要拿间接后代组件的尺寸位置来布局其他元素,在Compose里确实得用点针对性的小技巧,我给你一步步拆解怎么实现:
核心思路:用OnGloballyPositioned捕获目标组件的布局信息
Compose里提供了OnGloballyPositioned这个Modifier,它能在组件完成布局后拿到对应的LayoutCoordinates对象,里面包含了组件的尺寸、在不同坐标系下的位置等关键信息。咱们要做的就是把选中的那个Text的布局信息存起来,再传给下面的组件用。
具体实现步骤
添加状态保存布局信息
在你的Foo组件里,新增一个状态来存储选中Text的布局坐标,初始值设为null就行:var selectedTextLayoutInfo by remember { mutableStateOf<LayoutCoordinates?>(null) }给目标Text绑定布局监听
给LazyRow里的Text加上OnGloballyPositioned,但要注意只有当前选中的项才更新状态——毕竟LazyRow的Item是复用的,要是不做判断,滚动的时候其他复用的Item会覆盖掉选中项的信息,这是个很容易踩的坑:Text( word, color = if (indx == selected) Color.Red else Color.Black, modifier = Modifier.onGloballyPositioned { coordinates -> if (indx == selected) { selectedTextLayoutInfo = coordinates } } )传递布局信息给目标组件并计算布局
把selectedTextLayoutInfo传给ComponentWithWeiqhtOfSelectedTextAndPositionUnderIt,然后在这个组件里根据拿到的布局信息计算尺寸和位置。这里要注意坐标系的转换——因为Text嵌套在Box和LazyRow里,直接拿的位置是相对于父容器的,得转成相对于最外层Column的位置才准确:先给外层Column加个监听拿到它的布局坐标:
var columnLayoutInfo by remember { mutableStateOf<LayoutCoordinates?>(null) } Column(modifier = modifier.onGloballyPositioned { columnLayoutInfo = it }) { // ... 原有LazyRow代码 }然后在目标组件里处理:
@Composable fun ComponentWithWeiqhtOfSelectedTextAndPositionUnderIt( selectedTextLayoutInfo: LayoutCoordinates?, columnLayoutInfo: LayoutCoordinates? ) { // 处理初始空状态,比如给个默认尺寸占位 val textSize = selectedTextLayoutInfo?.size ?: Size.Zero // 转换坐标系,拿到Text相对于Column的位置 val textPositionInColumn = selectedTextLayoutInfo?.let { textCoords -> columnLayoutInfo?.let { columnCoords -> textCoords.localPositionOf(columnCoords, Offset.Zero) } ?: Offset.Zero } ?: Offset.Zero Box( modifier = Modifier .offset(x = textPositionInColumn.x, y = 8.dp) // 8dp是和上面Text的间距 .size(width = textSize.width.toDp(), height = 40.dp) // 复用Text的宽度,高度自定义 .background(Color.Blue.copy(alpha = 0.3f)) ) { Text("我在选中文字的正下方,宽度和它完全一致") } }
完整代码示例
把这些整合起来,你的Foo组件就变成这样了:
@Composable fun Foo(words: List<String>, modifier: Modifier = Modifier) { var selected by remember { mutableIntStateOf(0) } var selectedTextLayoutInfo by remember { mutableStateOf<LayoutCoordinates?>(null) } var columnLayoutInfo by remember { mutableStateOf<LayoutCoordinates?>(null) } Column(modifier = modifier.onGloballyPositioned { columnLayoutInfo = it }) { LazyRow { itemsIndexed(words) { indx, word -> Box( Modifier .clickable { selected = indx } .padding(8.dp) ) { Text( word, color = if (indx == selected) Color.Red else Color.Black, modifier = Modifier.onGloballyPositioned { coordinates -> if (indx == selected) { selectedTextLayoutInfo = coordinates } } ) } } } ComponentWithWeiqhtOfSelectedTextAndPositionUnderIt( selectedTextLayoutInfo = selectedTextLayoutInfo, columnLayoutInfo = columnLayoutInfo ) } } @Composable fun ComponentWithWeiqhtOfSelectedTextAndPositionUnderIt( selectedTextLayoutInfo: LayoutCoordinates?, columnLayoutInfo: LayoutCoordinates? ) { val textSize = selectedTextLayoutInfo?.size ?: Size.Zero val textPositionInColumn = selectedTextLayoutInfo?.let { textCoords -> columnLayoutInfo?.let { columnCoords -> textCoords.localPositionOf(columnCoords, Offset.Zero) } ?: Offset.Zero } ?: Offset.Zero Box( modifier = Modifier .offset(x = textPositionInColumn.x, y = 8.dp) .size(width = textSize.width.toDp(), height = 40.dp) .background(Color.Blue.copy(alpha = 0.3f)) ) { Text("我在选中文字的正下方,宽度和它完全一致") } }
额外注意点
- 当LazyRow滚动时,选中的Text位置变化,
OnGloballyPositioned会自动触发回调更新状态,下面的组件也会跟着重新布局,不用额外监听滚动。 - 要是你不需要精确到Text本身的尺寸,而是要Box的尺寸,也可以把
OnGloballyPositioned加到Box上,但你说不能减padding,那直接监听Text就最准确了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

