切换标签页后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() } } }
解决方案
问题根源
- 生命周期未正确绑定:切换标签页时,地图Composable被销毁,MapView从父容器移除,OSMdroid会自动调用
onPause停止瓦片加载;但返回时仅重新添加MapView,未触发onResume恢复加载,导致瓦片无法重新渲染,显示空网格。 - 单例代码逻辑冗余:
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
相关产品推荐
相关产品推荐

