Hello all, I am running into an issue when trying ...
# compose
b
Hello all, I am running into an issue when trying to create a MapView in compose. I am going to need to cluster my map markers, as such I need to use MapView instead of the new GoogleMap composable as it does not support clustering. When I navigate away from the map and then back to the map my markers do not show. code in reply…
On first init of map, my markers show, however when navigating away from and back to the map the marker does not show. I have verified in the debugger that the markers are being added to the map. Any ideas?
Copy code
@Composable
private fun Map(
   markers: List<MapMarker>
) {
   val context = LocalContext.current
   val mapView = remember { MapView(context) }
   MapLifecycle(mapView)

   var previousMarkers by remember { mutableStateOf(listOf<MapMarker>()) }
   var mapInitialized by remember { mutableStateOf(false) }

   LaunchedEffect(mapView, mapInitialized) {
      if (!mapInitialized) {
         val map = mapView.awaitMap()
         map.uiSettings.isMapToolbarEnabled = false
         mapInitialized = true
      }
   }

   LaunchedEffect(mapInitialized, markers) {
      if (mapInitialized) {
         val map = mapView.awaitMap()
         // add new
         // update existing
         // remove old
      }
   }

   AndroidView({ mapView })
}
@Composable
private fun MapLifecycle(mapView: MapView) {
   val context = LocalContext.current
   val lifecycle = LocalLifecycleOwner.current.lifecycle
   DisposableEffect(context, lifecycle, mapView) {
      val mapLifecycleObserver = mapView.lifecycleObserver()
      lifecycle.addObserver(mapLifecycleObserver)
      onDispose {
         lifecycle.removeObserver(mapLifecycleObserver)
      }
   }
}

private fun MapView.lifecycleObserver(): LifecycleEventObserver =
   LifecycleEventObserver { _, event ->
      when (event) {
         Lifecycle.Event.ON_CREATE -> this.onCreate(Bundle())
         Lifecycle.Event.ON_START -> this.onStart()
         Lifecycle.Event.ON_RESUME -> this.onResume()
         Lifecycle.Event.ON_PAUSE -> this.onPause()
         Lifecycle.Event.ON_STOP -> this.onStop()
         Lifecycle.Event.ON_DESTROY -> this.onDestroy()
         else -> throw IllegalStateException()
      }
   }
c
I just painstakingly went through this and im pretty happy with the result. I don't have any launched effects, I just use the AndroidView factory to do setup and then I save two things. 1. the clusterManager 2. the googleMap then in theAndroidView.onUpdate block I just call clusterManager.clear() and then add all my pins again. Lastly, when you do like clusterManager.addAll(viewModel.mypinsList) make sure to call clusterManager.addAll(viewModel.mypinsList.toList()) so that compose knows to track those mutableStateLists
b
Thanks, I can give that a go, but not sure that is what I want. I have more than 10K markers, and I don’t want to create and redraw all markers/clusters on every recompose. As such I would like to just monitor the markers last and do set math to handle only processing add/update/remove of markers in the cluster manager.
c
clustering is a big FR on the Maps Compose library. you can chime in or +1 this issue here https://github.com/googlemaps/android-maps-compose/issues/44 @Colton Idle also suggested that the sample repo have a demo of how to use clustering w/o maps compose—which we should definitely add. as far as not having to create/redraw markers on every recompose, that’s not currently possible. but, i think if
ClusterManager
had a
contains
function, you can at least do a check before adding an item
c
@Billy Newman I have about 4k markers and everything seems to work fine on my end, but 10k is def more than 4k. lol. Anyway. Just letting you know what I did. I think it should hopefully work well for as long as compose maps support clustering. im being hopeful and just guessing it'll be available in like 6months. do let me know if you do anything fancy with clusterManager.contains or anything. I could probably rig something up with that
contains
method.
b
@Chris Arriola I guess I don’t understand why you cannot keep track of marker changes and tell the cluster manager to add/update/remove rather than re creating the entire MapView and ClusterManager again. Something like this:
Copy code
var previousMarkers by remember { mutableStateOf(listOf<MapMarker>()) }
   var clusterManager by remember { mutableStateOf<ClusterManager?>(null)}
   LaunchedEffect(mapView, markers) {
      if (clusterManager == null) {
         val map = mapView.awaitMap().apply {
            uiSettings.isMapToolbarEnabled = false
         }
         clusterManager = // init cluster manager with map
      }

      var updateCluster = false
      val manager = clusterManager
      if (manager != null) {
         val markerSet = markers.toSet()
         val previousMarkerSet = previousMarkers.toSet()

         // add new
         markerSet.minus(previousMarkerSet).forEach {
            manager.addItem(it)
            updateCluster = true
         }

         // update existing
         markerSet.intersect(previousMarkerSet).forEach {
            manager.updateItem(it)
            updateCluster = true
         }

         // remove old
         previousMarkerSet.minus(markerSet).forEach {
            manager.removeItem(it)
            updateCluster = true
         }

         if (updateCluster) {
            manager.cluster()
            previousMarkers = markers
         }
      }
   }
c
that should work, too. so it looks like this works the first time around but then fails as soon as you navigate away and back to view the map? my hunch is that the issue could be related to lifecycle events not appropriately called when they should
b
@Chris Arriola, yes that is correct, and I am assuming the same thing. I have verified the same thing is happening without clustering, just a simple marker add and I run into the same problem. Hoping someone can point out the issue 🤞
c
I definitely had this issue when navigating away and coming back
Trick was reusing the googleMap from the AndroidView factory call in the AndroidView update call
I haven't looked at your code yet (sorry) but make sure you do that?
c
can you log the lifecycle events coming through? i’m curious which events are called in what sequence on the
MapView
. a possible workaround is to add a key to the
remember
for
MapView
so a new instance is created when you navigate back
b
@Chris Arriola sure thing; on initial navigation:
Copy code
I/Map lifecycle event: ON_CREATE
I/Map lifecycle event: ON_START
I/Map lifecycle event: ON_RESUME
on navigate away from map:
Copy code
I/Map lifecycle event: ON_PAUSE
I/Map lifecycle event: ON_STOP
on navigate back to map:
Copy code
I/Map lifecycle event: ON_CREATE
I/Map lifecycle event: ON_DESTROY
I/Map lifecycle event: ON_CREATE
I/Map lifecycle event: ON_START
I/Map lifecycle event: ON_RESUME
Interesting, so looks like on navigation back a additional create and destroy are called
c
ahh yeah looks like that’s what’s causing it. perhaps there’s 2 different lifecycles here? one gets destroyed and another gets created when you return to the screen.
b
Yeah, not really sure why yet…
c
@Billy Newman are you using tabs? I noticed some weirdness when using tabs and one of the tabs was a map
b
@Colton Idle I am not using tabs, but I am using compose navigation w/ navhost.
c
There's this existing issue on the maps Compose repo. Related? https://github.com/googlemaps/android-maps-compose/issues/112
b
@Chris Arriola great find, I am not using bottom nav, but I am using a navhost. I will try disabling cross fade animation to see what happens
👍 1
@Colton Idle, for sure some weirdness, I tried your solution, same issue.
@Colton Idle, @Chris Arriola looks like it is a navhost issue, replaced NavHost with AnimatedNavHost w/ no animation, indeed problem solved. Thank you both for you time an effort, I truly appreciate it.
c
i might have a workaround you can try without having to disable cross fade. see changes in https://github.com/googlemaps/android-maps-compose/pull/137
c
Interesting. I have a crossfade going without any issues, but i did have a bunch of issues like last week and all seemed resolved by some other steps I mustve taken. I will take a look into this stuff above though.
Looking at "Google Maps Wont restoreState properly when navigating through bottom nav #112" and it seems like I guess I hit this issue too but I wasn't using google-maps-compose... but yeah. I definitely got around this issue somehow. I think I might try to go for Chris' new POC of clustering he whipped up. https://github.com/googlemaps/android-maps-compose/issues/135
Actually I do get a crash using NavHost if I move quickly between tabs ^
Just FYI the fast switching issue via tabs seems to have been fixed in navigation-compose lib https://android-review.googlesource.com/c/platform/frameworks/support/+/2120138
c
Interesting
Thanks for sending over
👍 1
c
@Chris Arriola I do wonder if the fix you put in was un-necessary?
c
Ill have to test it, but perhaps
c
Yep. In the new nav compose artifact I believe.
c
Oh cool. I'll have to test that
Did you also encounter that bug? Feel free to test as well on an older version of maps Compose + newest nav compose version
c
Away on holiday the next few days, but yeah I was definitely getting the quick tab switch crash but I thought it was due to androidView + Google maps
c
The fix I put in definitely feels like a workaround for a nav-specific issue. Lookings at the CL you sent, it should fix the maps Compose bug without the workaround I placed
K 1