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

切换标签页后OSMdroid地图消失仅显示空网格问题求助

问题:OSMdroid单例MapView切换标签页后显示空网格

我尝试通过单例模式创建MapView来使用OSMdroid展示地图,首次进入地图标签页时显示正常,但切换到其他标签页再返回后,地图消失,仅能看到空网格。

相关代码如下:

单例MapView实现

object MapViewSingleton {
    private var mapView: MapView? = null

    fun getMapView(context: Context): MapView {
        if (mapView == null) {
            mapView = createMapView(context)
        }
        return mapView!!
    }

    private fun createMapView(context: Context): MapView {
        if (mapView != null) {
            if (mapView!!.parent != null) {
                (mapView!!.parent as ViewGroup).removeView(mapView)
            }
        }
        val mapView = MapView(context)

        Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
        Configuration.getInstance().userAgentValue = "Test"

        mapView.setTileSource(TileSourceFactory.MAPNIK)
        mapView.setMultiTouchControls(true)

        return mapView
    }
}

展示地图的Composable函数

@Composable
fun OsmdroidMapView() {
    val mapView = MapViewSingleton.getMapView(LocalContext.current)
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = {
            mapView
        }
    )
}

标签页实现代码

val items = listOf(
                BottomNavigationItem(
                    route = "settings",
                    title = "Einstellungen",
                    selectedIcon = Icons.Filled.Settings,
                    unselectedIcon = Icons.Outlined.Settings
                ),
                BottomNavigationItem(
                    route = "map",
                    title = "Karte",
                    selectedIcon = Icons.Filled.Place,
                    unselectedIcon = Icons.Outlined.Place
                ),
                BottomNavigationItem(
                    route = "data",
                    title = "Daten",
                    selectedIcon = Icons.Filled.Done,
                    unselectedIcon = Icons.Outlined.Done
                )
            )

            var selectedItemIndex by rememberSaveable { mutableStateOf(0) }
            val navController = rememberNavController()
            Scaffold(
                bottomBar = {
                    NavigationBar {
                        items.forEachIndexed { index, item ->
                            NavigationBarItem(
                                selected = selectedItemIndex == index,
                                onClick = {
                                    selectedItemIndex = index
                                    navController.navigate(item.route)
                                },
                                label = {
                                    Text(text = item.title)
                                },
                                icon = {
                                    BadgedBox(badge = {}) {
                                        Icon(
                                            imageVector = if (index == selectedItemIndex) {
                                                item.selectedIcon
                                            } else item.unselectedIcon,
                                            contentDescription = item.title
                                        )
                                    }
                                }
                            )
                        }
                    }
                }
            ) {
                NavHost(navController = navController, startDestination = "settings") {
                    composable("map") {
                        OsmdroidMapView()
                    }
                    composable("settings") {
                        Settings()
                    }
                    composable("data") {
                        Data()
                    }
                }
            }

解决方案

问题根源

  1. 生命周期未正确绑定:切换标签页时,地图Composable被销毁,MapView从父容器移除,OSMdroid会自动调用onPause停止瓦片加载;但返回时仅重新添加MapView,未触发onResume恢复加载,导致瓦片无法重新渲染,显示空网格。
  2. 单例代码逻辑冗余:createMapView中移除旧MapView父容器的代码永远不会执行(getMapView仅在mapView为null时调用该方法),属于无效逻辑。

方案1:放弃单例,让Composable管理MapView(推荐)

用remember保存MapView实例,绑定Composable生命周期,确保状态正确恢复:

@Composable
fun OsmdroidMapView() {
    val context = LocalContext.current
    // 用remember保留MapView实例,避免重组时重复创建
    val mapView = remember {
        MapView(context).apply {
            Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
            Configuration.getInstance().userAgentValue = "Test"
            setTileSource(TileSourceFactory.MAPNIK)
            setMultiTouchControls(true)
        }
    }

    // 绑定MapView生命周期,销毁时释放资源
    DisposableEffect(mapView) {
        onDispose {
            mapView.onDestroy()
        }
    }

    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { mapView },
        update = { view ->
            // 重新添加到容器时触发onResume,恢复瓦片加载
            view.onResume()
            view.invalidate()
        }
    )
}

方案2:修复单例逻辑(若必须保留单例)

修改单例类,确保每次获取MapView时恢复生命周期状态:

object MapViewSingleton {
    private var mapView: MapView? = null

    fun getMapView(context: Context): MapView {
        if (mapView == null) {
            mapView = createMapView(context)
        } else {
            // 恢复地图状态
            mapView?.onResume()
            mapView?.invalidate()
        }
        return mapView!!
    }

    private fun createMapView(context: Context): MapView {
        val mapView = MapView(context)
        Configuration.getInstance().load(context, PreferenceManager.getDefaultSharedPreferences(context))
        Configuration.getInstance().userAgentValue = "Test"
        mapView.setTileSource(TileSourceFactory.MAPNIK)
        mapView.setMultiTouchControls(true)
        return mapView
    }

    // 提供销毁方法,在应用退出时调用
    fun destroyMapView() {
        mapView?.onDestroy()
        mapView = null
    }
}

同时修改Composable,在销毁时暂停地图:

@Composable
fun OsmdroidMapView() {
    val mapView = MapViewSingleton.getMapView(LocalContext.current)
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { mapView },
        update = {
            it.onResume()
            it.invalidate()
        }
    )

    // 离开页面时暂停地图,节省资源
    DisposableEffect(Unit) {
        onDispose {
            mapView.onPause()
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 15:17:35