How to scroll/zoom a map inside a vertically scrolling in jetpack compose?

Viewed 555

I am implementing a list of elements that I show in a column with vertical scroll. Each element contains a map. The problem is that on these maps I cannot zoom with finger gestures, nor can I scroll vertically on these maps. Horizontally I can move, but the movement is not fluid. It is as if the vertical scroll of the column is affecting the interaction with the map. Here is the code in case anyone can help me:

Main:

LazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .padding(vertical = 8.dp)
) {
    items(state.data.elements) { element ->
        ElementMap(
            lat = element.lat,
            lon = element.lon
        )
    }
}

ElementMap:

import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.MapView

fun ElementMap(
    lat: Double,
    lon: Double
) {

    val mapView = rememberMapViewWithLifeCycle()

    Column(
        modifier = Modifier
            .background(Color.White)
    ) {
        AndroidView(
            {
                mapView
            }
        ) {
            mapView.getMapAsync {
                val map = it
                map.uiSettings.isZoomControlsEnabled = false
                map.addMarker(marker(lat, lon, 16f, 250f))
                map.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(lat, lon), 16f))
                map.mapType = GoogleMap.MAP_TYPE_HYBRID
            }
        }
    }
}

@Composable
fun rememberMapViewWithLifeCycle(): MapView {
    val context = LocalContext.current
    val mapView = remember {
        MapView(context).apply {
            id = R.id.map_frame
        }
    }
    val lifeCycleObserver = rememberMapLifecycleObserver(mapView)
    val lifeCycle = LocalLifecycleOwner.current.lifecycle
    DisposableEffect(lifeCycle) {
        lifeCycle.addObserver(lifeCycleObserver)
        onDispose {
            lifeCycle.removeObserver(lifeCycleObserver)
        }
    }

    return mapView
}

@Composable
fun rememberMapLifecycleObserver(mapView: MapView): LifecycleEventObserver =
    remember(mapView) {
        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()
            }
        }
    }

I have tried to show a map in full screen, that is, outside the vertical scroll, and in this way the gestures work correctly, I can zoom as well as scroll in all directions. Therefore, it seems to be a problem of having a map inside a scroll, but I'm not sure how to solve this.

1 Answers

I have managed to prevent the map from losing touch events due to scrolling. I have created a class of my own implementing MapView (in package com.google.android.gms.maps) as stated in this answer.

class CustomMapView(context: Context) : MapView(context) {

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        when (ev.action) {
            MotionEvent.ACTION_UP   -> parent.requestDisallowInterceptTouchEvent(false)
            MotionEvent.ACTION_DOWN -> parent.requestDisallowInterceptTouchEvent(true)
        }
        return super.dispatchTouchEvent(ev)
    }
}

So instead of using MapView I use my CustomMapView class and it works perfectly.

Related