https://kotlinlang.org logo
#compose
Title
# compose
b

Billy Newman

06/06/2022, 3:42 PM
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

Colton Idle

06/06/2022, 4:00 PM
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

Billy Newman

06/06/2022, 4:37 PM
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

Chris Arriola

06/06/2022, 5:11 PM
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

Colton Idle

06/06/2022, 5:14 PM
@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

Billy Newman

06/06/2022, 5:48 PM
@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

Chris Arriola

06/06/2022, 5:59 PM
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

Billy Newman

06/06/2022, 6:15 PM
@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

Colton Idle

06/06/2022, 6:19 PM
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

Chris Arriola

06/06/2022, 6:24 PM
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

Billy Newman

06/06/2022, 6:43 PM
@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

Chris Arriola

06/06/2022, 8:41 PM
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

Billy Newman

06/06/2022, 9:45 PM
Yeah, not really sure why yet…
c

Colton Idle

06/06/2022, 11:02 PM
@Billy Newman are you using tabs? I noticed some weirdness when using tabs and one of the tabs was a map
b

Billy Newman

06/07/2022, 12:25 AM
@Colton Idle I am not using tabs, but I am using compose navigation w/ navhost.
c

Chris Arriola

06/07/2022, 12:33 AM
There's this existing issue on the maps Compose repo. Related? https://github.com/googlemaps/android-maps-compose/issues/112
b

Billy Newman

06/07/2022, 2:00 AM
@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

Chris Arriola

06/07/2022, 4:25 AM
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

Colton Idle

06/07/2022, 10:32 AM
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

Chris Arriola

06/18/2022, 5:14 AM
Interesting
Thanks for sending over
👍 1
c

Colton Idle

06/20/2022, 3:12 PM
@Chris Arriola I do wonder if the fix you put in was un-necessary?
c

Chris Arriola

06/20/2022, 3:32 PM
Ill have to test it, but perhaps
c

Colton Idle

06/20/2022, 3:36 PM
Yep. In the new nav compose artifact I believe.
c

Chris Arriola

06/20/2022, 3:42 PM
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

Colton Idle

06/20/2022, 3:43 PM
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

Chris Arriola

06/20/2022, 3:46 PM
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
31 Views