maplibre / maplibre-native

MapLibre Native - Interactive vector tile maps for iOS, Android and other platforms.
https://maplibre.org
BSD 2-Clause "Simplified" License
1.01k stars 297 forks source link

[Android] Is anyone planning to put mapview in Compose? #344

Open ravenfeld opened 2 years ago

ravenfeld commented 2 years ago

Hi, I'm going to start an application in Android Compose. I wanted to know if the project will put the view in Compose or if there is an example to know how to do it.

Thanks in advance.

wipfli commented 2 years ago

Hi @ravenfeld I have not used compose before. What is it for? If you don't find documentation on how to do this, feel free to start a new readme or so and submit a pull request...

ravenfeld commented 2 years ago

https://developer.android.com/jetpack/compose

This is a new way to create views on Android. It's a bit like Swift UI I think.

PiotrPrus commented 2 years ago

I am using maplibre in compose and it is working flawlessly. I have checked how google made the GoogleMap composable function and mimic that solution. Underneath it is just an AndroidView composable, which is interop for classic XML. Here are some utils that I am using: https://gist.github.com/PiotrPrus/d65378c36b0a0c744e647946f344103c and here is a small sample of using it:

val map = rememberMapViewWithLifecycle()           

AndroidView(
                modifier = Modifier.fillMaxSize(),
                factory = { map },
                update = { mapView ->
                    coroutineScope.launch {
                        val libreMap = mapView.awaitMap()
                        libreMap.setStyle(
                            Style.Builder().fromJson(
                                ResourceReader.readRawResource(
                                    context,
                                    R.raw.mapbox_style
                                ) ?: ""
                            )
                        )
                  }
)
ravenfeld commented 2 years ago

thanks a lot, that's what I was starting to look at. I'm just having trouble with the listeners doing leaks

PiotrPrus commented 2 years ago

which listeners? Maybe you need to observe the compose lifecycle or make some activity listeners and observe them in slide effects?

ravenfeld commented 1 year ago

Sorry I didn't see your message. I also looked at Google Map and they did a lot of things on their side and not just put in an AndroidView. I don't know what the performance impact is. And my code for drawing lines on the map is not very clean at the moment I think. Google map exposed the components in Composable on their side.

daniel-j-h commented 1 year ago

Hey folks, I'm using the official Mapbox SDK for Android at the moment in a Jetpack Compose app and had to adapt it to make it work. Here's what I'm doing more or less: https://gist.github.com/daniel-j-h/cd88b6cdf54e86bffed2bc440cbbb751

By now there is Google Maps for Compose, with a nice API; easy to use

The official Mapbox SDK for Android still does not provide a similar API for Compose; there's an in-progress pull request upstream from summer 2022 but there seems to be no progress from what I can tell.

Ideally we would have a Google Maps for Compose like API and beat Mapbox to a great developing experience going forward :)

ravenfeld commented 1 year ago

Thank you very much, I have started a work with Mapbox and we are relasioning with them. I am in the process of getting mapbox into compose but not sure if my company will allow me to do the same in opensource for maplibre.

JonasVautherin commented 1 year ago

@RomanBapst and I have been working on such a library, both for MapLibre and Mapbox. It is called ramani-maps: https://github.com/ramani-maps/ramani-maps.

It does not have all the features (we added what we use first, obviously), but we intend to keep adding features steadily, and we are open to contributions :blush:.

I hope this helps!

wissa-b commented 12 months ago

I am using maplibre in compose and it is working flawlessly. I have checked how google made the GoogleMap composable function and mimic that solution. Underneath it is just an AndroidView composable, which is interop for classic XML. Here are some utils that I am using: https://gist.github.com/PiotrPrus/d65378c36b0a0c744e647946f344103c and here is a small sample of using it:

val map = rememberMapViewWithLifecycle()           

AndroidView(
                modifier = Modifier.fillMaxSize(),
                factory = { map },
                update = { mapView ->
                    coroutineScope.launch {
                        val libreMap = mapView.awaitMap()
                        libreMap.setStyle(
                            Style.Builder().fromJson(
                                ResourceReader.readRawResource(
                                    context,
                                    R.raw.mapbox_style
                                ) ?: ""
                            )
                        )
                  }
)

I wouldn't set the style in the update maybe better in the Factory? so it's not called every time we want to update markers or camera position. but probably this is just an example, it's very helpful, Thank you!

ravenfeld commented 12 months ago

It's not as simple as that: you also have to follow the lifecycle to warn a map. Looking at google maps, that's what they do. The project above does it very well.

ianthetechie commented 9 months ago

It seems like there is an issue with NativeConnectivityListener when trying to use MapLibre in Compose Previews.

image

Digging into this, it looks like it's some sort of native loader, which shouldn't be an issue inherently, but I am starting to realize that Compose Previews have several non-obvious limitations, including network and certain file APIs being unavailable.

Has anyone else gotten Compose Previews to work yet? This is clearly the modern way to do Android development, and not being able to use MapLibre here would be unfortunate.

Here is my code so far.

/**
 * Remembers a MapView and gives it the lifecycle of the current LifecycleOwner
 */
@Composable
fun rememberMapViewWithLifecycle(): MapView {
    val context = LocalContext.current
    val mapView = remember { MapView(context) }

    // Makes MapView follow the lifecycle of this composable
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(lifecycle, mapView) {
        val lifecycleObserver = getMapLifecycleObserver(mapView)
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
        }
    }

    return mapView
}

private fun getMapLifecycleObserver(mapView: MapView): LifecycleEventObserver =
    LifecycleEventObserver { _, event ->
        when (event) {
//            Lifecycle.Event.ON_CREATE -> mapView.onCreate(Bundle())
            Lifecycle.Event.ON_START -> mapView.onStart()
            Lifecycle.Event.ON_RESUME -> mapView.onResume()
            Lifecycle.Event.ON_PAUSE -> mapView.onPause()
            Lifecycle.Event.ON_STOP -> mapView.onStop()
            Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
            else -> throw IllegalStateException()
        }
    }

@Composable
fun NavigationMapView() {
    val mapView = rememberMapViewWithLifecycle()

    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { mapView },
        update = { mapView ->
            mapView.getMapAsync { map ->
                map.setStyle("https://demotiles.maplibre.org/style.json")
            }
        }
    )
}

@Preview
@Composable
fun PreviewNavigationMap() {
    Mapbox.getInstance(LocalContext.current)
    NavigationMapView()
}

I've attempted to do some crimes against the JVM and use ByteBuddy to replace the class at runtime, but am like 12 layers deep into weeds and way out of my range of comfort with Java. If my hunch is correct, it seems like MapLibre should allow for a mock connectivity listener implementation to enable previews, and I was hoping to confirm quickly with hacks, but that's proving to be difficult 😅

mtpdog commented 7 months ago

Are there any plans to add native Jetpack Compose support?

louwers commented 7 months ago

I spoke with @JonasVautherin and I think I remember he said there are not really any disadvantages having it as a separate project. So for now I recommend using Ramani Maps.

Long term it makes sense to have Jetpack Compose support and if anyone wants to integrate this functionality a PR would be welcome.

JonasVautherin commented 7 months ago

Yeah, I really don't see any disadvantage, and I don't see how it could be done in a more "native" way than what Ramani Maps does. I could see a governance issue (i.e. if you have a problem using Ramani Maps because it is not officially owned by the MapLibre project), but technically I think it is as native as it gets (well "more native" could be by calling C++ directly instead of using MapLibre Android, but IMHO it would be a poor idea).

mtpdog commented 7 months ago

Ramani Maps looks promising. However being a separate project it probably won't get the latest beta or pre-release versions of Maplibre..

JonasVautherin commented 7 months ago

However being a separate project it probably won't get the latest beta or pre-release versions of Maplibre...

Can't you just tell gradle to point to a different version of the dependency when including org.ramani-maps:ramani-maplibre, e.g. like this? :thinking:

ianthetechie commented 7 months ago

Yes. Gradle is a complex beast to understand, but this is totally possible. And doesn’t require merging higher level wrappers like Ramani or the similar SwiftUI project into the main repo.

mtpdog commented 7 months ago

However being a separate project it probably won't get the latest beta or pre-release versions of Maplibre...

Can't you just tell gradle to point to a different version of the dependency when including org.ramani-maps:ramani-maplibre, e.g. like this? 🤔

Thanks! Indeed, it looks like a good option.