4

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.

Paul9999
  • 751
  • 1
  • 11
  • 26

2 Answers2

0

this workaround work pretty well. reference https://github.com/googlemaps/android-maps-compose/blob/main/app/src/main/java/com/google/maps/android/compose/MapInColumnActivity.kt

class MapInColumnActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        // Observing and controlling the camera's state can be done with a CameraPositionState
        val cameraPositionState = rememberCameraPositionState {
            position = defaultCameraPosition
        }
        var columnScrollingEnabled by remember { mutableStateOf(true) }

        // Use a LaunchedEffect keyed on the camera moving state to enable column scrolling when the camera stops moving
        LaunchedEffect(cameraPositionState.isMoving) {
            if (!cameraPositionState.isMoving) {
                columnScrollingEnabled = true
                Log.d(TAG, "Map camera stopped moving - Enabling column scrolling...")
            }
        }

        MapInColumn(
            modifier = Modifier.fillMaxSize(),
            cameraPositionState,
            columnScrollingEnabled = columnScrollingEnabled,
            onMapTouched = {
                columnScrollingEnabled = false
                Log.d(
                    TAG,
                    "User touched map - Disabling column scrolling after user touched this Box..."
                )
            },
            onMapLoaded = { }
        )
    }
}

}

    @Composable

fun MapInColumn(
    modifier: Modifier = Modifier,
    cameraPositionState: CameraPositionState,
    columnScrollingEnabled: Boolean,
    onMapTouched: () -> Unit,
    onMapLoaded: () -> Unit,
) {
    Surface(
        modifier = modifier,
        color = MaterialTheme.colors.background
    ) {
        var isMapLoaded by remember { mutableStateOf(false) }

        Column(
            Modifier
                .fillMaxSize()
                .verticalScroll(
                    rememberScrollState(),
                    columnScrollingEnabled
                ),
            horizontalAlignment = Alignment.Start
        ) {
            Spacer(modifier = Modifier.padding(10.dp))
            for (i in 1..20) {
                Text(
                    text = "Item $i",
                    modifier = Modifier
                        .padding(start = 10.dp)
                        .testTag("Item $i")
                )
            }
            Spacer(modifier = Modifier.padding(10.dp))

            Box(
                Modifier
                    .fillMaxWidth()
                    .height(200.dp)
            ) {
                GoogleMapViewInColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .testTag("Map")
                        .pointerInteropFilter(
                            onTouchEvent = {
                                when (it.action) {
                                    MotionEvent.ACTION_DOWN -> {
                                        onMapTouched()
                                        false
                                    }
                                    else -> {
                                        Log.d(
                                            TAG,
                                            "MotionEvent ${it.action} - this never triggers."
                                        )
                                        true
                                    }
                                }
                            }
                        ),
                    cameraPositionState = cameraPositionState,
                    onMapLoaded = {
                        isMapLoaded = true
                        onMapLoaded()
                    },
                )
                if (!isMapLoaded) {
                    androidx.compose.animation.AnimatedVisibility(
                        modifier = Modifier
                            .fillMaxSize(),
                        visible = !isMapLoaded,
                        enter = EnterTransition.None,
                        exit = fadeOut()
                    ) {
                        CircularProgressIndicator(
                            modifier = Modifier
                                .background(MaterialTheme.colors.background)
                                .wrapContentSize()
                        )
                    }
                }
            }
            Spacer(modifier = Modifier.padding(10.dp))
            for (i in 21..40) {
                Text(
                    text = "Item $i",
                    modifier = Modifier
                        .padding(start = 10.dp)
                        .testTag("Item $i")
                )
            }
            Spacer(modifier = Modifier.padding(10.dp))
        }
    }
}

    @Composable

private fun GoogleMapViewInColumn(
    modifier: Modifier,
    cameraPositionState: CameraPositionState,
    onMapLoaded: () -> Unit,
) {
    val singaporeState = rememberMarkerState(position = singapore)

    var uiSettings by remember { mutableStateOf(MapUiSettings(compassEnabled = false)) }
    var mapProperties by remember {
        mutableStateOf(MapProperties(mapType = MapType.NORMAL))
    }

    GoogleMap(
        modifier = modifier,
        cameraPositionState = cameraPositionState,
        properties = mapProperties,
        uiSettings = uiSettings,
        onMapLoaded = onMapLoaded
    ) {
        // Drawing on the map is accomplished with a child-based API
        val markerClick: (Marker) -> Boolean = {
            Log.d(TAG, "${it.title} was clicked")
            cameraPositionState.projection?.let { projection ->
                Log.d(TAG, "The current projection is: $projection")
            }
            false
        }
        MarkerInfoWindowContent(
            state = singaporeState,
            title = "Singapore",
            onClick = markerClick,
            draggable = true,
        ) {
            Text(it.title ?: "Title", color = Color.Red)
        }
    }
}
-2

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.

Paul9999
  • 751
  • 1
  • 11
  • 26