You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开启TalkBack时Jetpack Compose Map抛出LayoutNode异常求助

问题定位与修复方案

问题原因

开启TalkBack后触发的java.lang.IllegalStateException: LayoutNode should be attached to an owner异常,核心是Google Map组件还没完成布局附着到Compose树时,就执行了CameraPositionState的动画操作。

TalkBack会触发Compose语义树的额外构建流程,导致组件附着时机延迟,而原代码直接依赖LocalConfiguration计算地图高度,且在LaunchedEffect中无条件执行相机动画,在无障碍模式下触发了时序冲突。

修复步骤

1. 优化地图高度计算逻辑

放弃硬编码的高度计算方式,改用Compose原生布局实现上下分区,避免因Configuration变化或布局时序问题引发异常:

// 用fillMaxSize + bottom padding替代硬编码高度计算
GoogleMap(
    modifier = Modifier
        .fillMaxSize()
        .padding(bottom = 308.dp),
    // ...其他参数不变
)

2. 确保相机动画仅在组件就绪后执行

在LaunchedEffect中增加cameraState.isAttached判断,只有当CameraPositionState已经和Google Map绑定后,才执行动画:

LaunchedEffect(key1 = currentLoc, key2 = cameraState.isAttached) {
    if (cameraState.isAttached) {
        cameraState.centerOnLocation(currentLoc)
    }
}

3. 完整修复代码

@Composable
fun LocationPickerScreen(
    uiState: LocationPickerScreenContract.UiState,
) {
    val currentLoc = uiState.locationInfo.addressLatLng
    val cameraState = rememberCameraPositionState()

    LaunchedEffect(key1 = currentLoc, key2 = cameraState.isAttached) {
        if (cameraState.isAttached) {
            cameraState.centerOnLocation(currentLoc)
        }
    }

    val marker = LatLng(currentLoc.latitude, currentLoc.longitude)

    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colors.background
    ) {
        Box(modifier = Modifier.fillMaxSize()) {
            GoogleMap(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(bottom = 308.dp),
                cameraPositionState = cameraState,
                properties = MapProperties(
                    isMyLocationEnabled = true,
                    mapType = MapType.HYBRID,
                    isTrafficEnabled = true
                )
            ) {
                Marker(
                    state = MarkerState(position = marker),
                    title = "MyPosition",
                    snippet = "This is a description of this Marker",
                    draggable = true
                )
            }

            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(308.dp)
                    .align(Alignment.BottomCenter),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center,
            ) {
                Text(
                    text = "Data Place Holder",
                    modifier = Modifier
                        .semantics { contentDescription = "data" }
                        .padding(bottom = 8.dp),
                    style = MaterialTheme.typography.body2.copy(
                        fontSize = 14.sp,
                        fontWeight = FontWeight.SemiBold,
                        lineHeight = 16.sp
                    ),
                )
            }
        }
    }
}

private suspend fun CameraPositionState.centerOnLocation(location: LatLng) = animate(
    update = CameraUpdateFactory.newLatLngZoom(location, 15f),
    durationMs = 1500
)

额外说明

  • 原代码硬编码高度的方式不仅会触发无障碍模式下的崩溃,还会导致屏幕旋转、分屏等场景下的布局适配问题,改用padding实现分区更符合Compose布局规范。
  • TalkBack模式下Compose会优先处理语义信息,组件的布局附着会延迟,因此所有依赖组件状态的操作必须先校验组件就绪状态。

内容的提问来源于stack exchange,提问作者Sam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 06:48:39