Issue
in the official documentation for Mapbox SDK for android there is a simple example of map using standard UI library for Android. Full documentation for completeness of the question can be found here.
The code mentioned there is like this:
private var mapView: MapView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this, getString(R.string.mapbox_access_token))
setContentView(R.layout.activity_main)
mapView = findViewById(R.id.mapView)
mapView?.onCreate(savedInstanceState)
mapView?.getMapAsync { mapboxMap ->
mapboxMap.setStyle(Style.MAPBOX_STREETS) {
// Map is set up and the style has loaded. Now you can add data or make other map adjustments
}
}
}
Code for handling activity and mapbox map lifecycle events is like this:
override fun onStart() {
super.onStart()
mapView?.onStart()
}
override fun onResume() {
super.onResume()
mapView?.onResume()
}
override fun onPause() {
super.onPause()
mapView?.onPause()
}
override fun onStop() {
super.onStop()
mapView?.onStop()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView?.onSaveInstanceState(outState)
}
override fun onLowMemory() {
super.onLowMemory()
mapView?.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView?.onDestroy()
}
My question is, if one is using mapbox map in jetpack compose, are those events of the map dealt with automatically, or does developer need to handle them himself? I am asking, because I do not want to have any doubts how to deal with the best practices regarding the AndroidView in jetpack compose especially when the map is placed in composable outside of any particular activity or fragment.
Jetpack compose code snippet for completeness:
@Composable
fun MapWithFab() {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val mapboxMap = createRef()
val fab = createRef()
AndroidView(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 35.dp)
.constrainAs(mapboxMap) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
},
factory = { context ->
Mapbox.getInstance(
context,
context.getString(R.string.mapbox_access_token)
)
MapView(context).apply {
getMapAsync { mapboxMap ->
mapboxMap.setStyle(Style.MAPBOX_STREETS)
val position = CameraPosition.Builder()
.target(LatLng(51.04004014308637, 13.744085852141072))
.zoom(15.0)
.build()
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1)
}
}
}
)
FloatingActionButton(
onClick = { },
modifier = Modifier
.padding(25.dp)
.width(50.dp)
.height(50.dp)
.constrainAs(fab) {
end.linkTo(mapboxMap.end)
bottom.linkTo(mapboxMap.bottom)
}
) {
}
}
}
Thank you for your answers and ideas.
Solution
It turns out to be mentioned in one of the example projects on compose right from the google, the code and the project on github can be found here
It can be done something like this:
@Composable
fun MapWrapper() {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val mapboxMap = createRef()
val fab = createRef()
val mapView = rememberMapViewWithLifecycle()
AndroidView(
factory = {mapView},
modifier = Modifier.constrainAs(mapboxMap) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
})
FloatingActionButton(
onClick = {
},
modifier = Modifier
.padding(25.dp)
.width(50.dp)
.height(50.dp)
.constrainAs(fab) {
end.linkTo(mapboxMap.end)
bottom.linkTo(mapboxMap.bottom)
}
) {
}
}
}
@Composable
fun rememberMapViewWithLifecycle(): MapView {
val context = LocalContext.current
val mapView = remember {
Mapbox.getInstance(
context,
context.getString(R.string.mapbox_access_token)
)
MapView(context).apply {
val mapView = this
getMapAsync { mapboxMap ->
mapboxMap.setStyle(Style.MAPBOX_STREETS)
val position = CameraPosition.Builder()
.target(LatLng(70.04004014308637, -20.744085852141072))
.zoom(15.0)
.build()
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1)
mapboxMap.getStyle {
}
}
}
}
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle, mapView) {
// Make MapView follow the current lifecycle
val lifecycleObserver = getMapLifecycleObserver(mapView)
lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycle.removeObserver(lifecycleObserver)
}
}
return mapView
}
/**
* Handles lifecycle of provided 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()
}
}
It can be implemented this way for pretty much any AndroidView, which needs to be responsive for the lifecycle events. The only thing which this implementation lacks is that there is no handling of onLowMemory() case.
Update - Note: The solution above is solving the lifecycle for MapboxMaps v9, MapboxMaps v10 already have it integrated.
Answered By - Rickertbrandsen
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.