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

添加大量AdvancedMarker至Maps时如何显示CircularProgressIndicator?

问题根源分析

你的代码里有两个核心问题导致进度条无法显示:

  1. UI线程阻塞:GoogleMap的content lambda是在UI线程执行的,循环创建大量AdvancedMarker会直接卡住UI线程——进度条的渲染还没来得及执行,就被循环阻塞,等循环结束后又立刻把loading设为false,进度条根本没机会显示。
  2. 错误的状态时机:在GoogleMap的content里直接调用vm.setLoading(true/false),会在Compose每次重组时重复执行,状态切换逻辑完全混乱,无法正确触发进度条的显示/隐藏。
解决方案

1. ViewModel层:后台处理标记数据,正确控制加载状态

把标记数据的预处理(或准备逻辑)放到后台线程,避免阻塞UI,同时通过StateFlow管理加载状态:

class MapViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(MapUiState())
    val uiState = _uiState.asStateFlow()

    // 加载标记数据,后台执行避免UI阻塞
    fun loadMarkers(rawData: List<YourMarkerData>) {
        viewModelScope.launch(Dispatchers.IO) {
            // 开始加载,标记loading为true
            _uiState.update { it.copy(loading = true) }
            
            // 这里可以做耗时的预处理(比如转换坐标、过滤数据等)
            val processedMarkers = rawData.map { 
                MarkerItem(it.lat, it.lon, it.name) 
            }
            
            // 加载完成,更新状态
            _uiState.update {
                it.copy(
                    loading = false,
                    markers = processedMarkers
                )
            }
        }
    }

    data class MapUiState(
        val loading: Boolean = false,
        val markers: List<MarkerItem> = emptyList()
    )

    data class MarkerItem(val lat: Double, val lon: Double, val name: String)
}

2. UI层:根据状态渲染地图和进度条

在UI中通过collectAsStateWithLifecycle监听状态,只在数据准备好后渲染标记,同时根据loading状态显示进度条:

@Composable
fun MapScreen(vm: MapViewModel = viewModel()) {
    val uiState by vm.uiState.collectAsStateWithLifecycle()
    val cameraPositionState = rememberCameraPositionState()

    Box(modifier = Modifier.fillMaxSize()) {
        GoogleMap(
            modifier = Modifier.fillMaxSize(),
            cameraPositionState = cameraPositionState
        ) {
            // 仅当标记数据准备好后才渲染,避免UI线程阻塞
            uiState.markers.forEach { item ->
                AdvancedMarker(
                    state = rememberMarkerState(position = LatLng(item.lat, item.lon)),
                    title = item.name
                )
            }
        }

        // 加载状态显示遮罩和进度条
        if (uiState.loading) {
            Box(
                modifier = Modifier.fillMaxSize()
                    .background(Color.White.copy(alpha = 0.5f))
                    .clickable(enabled = false) {} // 防止点击穿透到地图
            )
            CircularProgressIndicator(
                modifier = Modifier.fillMaxSize()
                    .wrapContentSize(Alignment.Center)
            )
        }
    }

    // 屏幕初始化时触发标记加载
    LaunchedEffect(Unit) {
        // 替换成你的原始数据源
        val rawMarkerData = fetchYourMarkerData()
        vm.loadMarkers(rawMarkerData)
    }
}

关键优化点

  • 后台线程处理:把耗时的标记数据预处理放到Dispatchers.IO,保证UI线程不被阻塞,进度条能正常渲染。
  • 状态驱动UI:通过StateFlow管理加载状态和标记数据,UI完全根据状态自动重组,避免手动在UI逻辑中切换状态。
  • 避免重复执行:用LaunchedEffect(Unit)触发一次加载逻辑,不会在每次重组时重复调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:55:58