Jason
Jason

Reputation: 647

Place tap listener on Google maps android

I have an android app with a map fragment. The map displays places and I am looking for a way to add a listener to get that place when the user taps on them.

The most relevant information I found was a suggestion to add a listener for clicks to the map, get the long&lat and then search for a place based on that. I thought I could do that using FetchPlaceRequest but this also seems to require a placeId in the first place when instantiating.

Am I missing something really basic?

EDIT

The code for the fragment containing the map (I thought implementing PlaceSelectionlistener would do the work)

class MapFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener,
PlaceSelectionListener, KoinComponent {

private lateinit var mapViewModel: MapViewModel
private lateinit var map: GoogleMap
private var fusedLocationClient: FusedLocationProviderClient? = null
private var locationRequest: LocationRequest? = null
private var locationCallback: LocationCallback? = null
private val permissionsUtils : PermissionsUtils by inject()
private val preferencesUtils : PreferencesUtils by inject { parametersOf(activity!!.applicationContext)}
private var root : View? = null
private val defaultZoom : Float = 16f

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    mapViewModel = ViewModelProviders.of(this).get(MapViewModel::class.java)
    root = inflater.inflate(R.layout.fragment_map, container, false)

    if (canAccessLocation()) {
        initialiseMap(true)
    } else {
        val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)

        requestPermissions(permissions, PermissionRequests.FineLocation.value)
    }

    return root
}

override fun onMarkerClick(p0: Marker?) = false

override fun onMapReady(googleMap: GoogleMap) {
    map = googleMap

    map.setMapStyle(MapStyleOptions
        .loadRawResourceStyle(activity!!.applicationContext, preferencesUtils.mapMode))

    map.uiSettings.isZoomControlsEnabled = true

    if (canAccessLocation()){
        map.isMyLocationEnabled = true
        map.uiSettings.isMyLocationButtonEnabled = true
    }

    map.setOnMarkerClickListener(this)
}

override fun onRequestPermissionsResult(requestCode: Int,
                                        permissions: Array<String>, grantResults: IntArray) {
    when (requestCode) {

        PermissionRequests.FineLocation.value -> {
            val permissionGranted = grantResults.isNotEmpty()
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED

            initialiseMap(permissionGranted)
        }
    }
}

override fun onPause() {
    super.onPause()
    fusedLocationClient?.removeLocationUpdates(locationCallback)
}

override fun onResume() {
    super.onResume()

    requestLocationUpdates()
}

override fun onPlaceSelected(status: Place) {
    val toast = Toast.makeText(activity!!.applicationContext,""+ status!!.name + status!!.latLng, Toast.LENGTH_LONG)
    toast.setGravity(Gravity.TOP, 0, 0)
    toast.show()
}

override fun onError(status: Status) {
    Toast.makeText(activity!!.applicationContext,"" + status.toString(), Toast.LENGTH_LONG)
        .show()
}

private fun initialiseMap(withLocation: Boolean) {
    val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment

    mapFragment.getMapAsync(this)

    if (!withLocation) {
        return
    }

    requestLocationUpdates()
}

private fun requestLocationUpdates() {
    if (fusedLocationClient == null) {
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(activity!!.applicationContext)
    }

    locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult) {
            val locationList = locationResult.locations

            if (locationList.size > 0) {
                val location = locationList[locationList.size - 1]
                val latLng = LatLng(location.latitude, location.longitude)

                map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, defaultZoom))
            }
        }
    }

    fusedLocationClient?.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper())

    locationRequest = LocationRequest()
    locationRequest?.interval = 1800000
    locationRequest?.fastestInterval = 1800000
    locationRequest?.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY

    fusedLocationClient?.lastLocation?.addOnCompleteListener(Activity()) { task ->
            if (task.isSuccessful && task.result != null) {
                val latLong = LatLng(task.result!!.latitude, task.result!!.longitude)
                map.moveCamera(CameraUpdateFactory.newLatLngZoom(latLong, defaultZoom))
            }
        }
}

private fun canAccessLocation(): Boolean {
    return permissionsUtils.hasPermission(activity!!.applicationContext, Manifest.permission.ACCESS_FINE_LOCATION)
}
}

Upvotes: 1

Views: 295

Answers (1)

Andrii Omelchenko
Andrii Omelchenko

Reputation: 13343

Because you can't manage places that shown on MapView (MapFragment) and it's markers not clickable (and customizable) IMHO better way is to hide "default" place markers via Google Maps styling like in this answer of Jozef:

  1. Create JSON file src\main\res\raw\map_style.json like this:

[
  {
    featureType: "poi",
    elementType: "labels",
    stylers: [
      {
        visibility: "off"
      }
    ]
  }
]
  1. Add map style to your GoogleMap

googleMap.setMapStyle(MapStyleOptions.loadRawResourceStyle(getContext(), R.raw.map_style));

and then - get nearby places via Place Search from Google Places API URL request:

https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=<LAT_LNG>&types=point_of_interest&radius=<RADIUS_IN_METERS>&sensor=false&key=<YOUR_APP_KEY>

parse it and show desired places on map programmatically as customizable and clickable Google Maps markers. That approach allows to you not only process marker clicks via default onMarkerClick(), but to manage quantity and types of places, marker icons design etc. There is also no need to create a request and process its response every time a user clicks on map.

NB! Nearby URL request returns only 20 places, for load more data you should use string value from next_page_token tag of response and pass it via pagetoken parameter for next request:

https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=<LAT_LNG>&types=point_of_interest&radius=<RADIUS_IN_METERS>&sensor=false&key=<YOUR_APP_KEY>&pagetoken=<TOKEN_FOR_NEXT_PAGE_FROM_next_page_token_TAG_OF_RESPONSE>

Upvotes: 1

Related Questions