I want to build an interactive Android map app. It will have different marker types and lots of of different options when clicking on them.
First approach :
I started with the notion I will use custom infowindows but figured out that a map can have only single InfoWindowAdapter, with that said, this approach has another fault. InfoWindows can't have click listeners registered to them and I need to have some clickable UI to show after marker click.
Second approach :
Marker click triggers an alertDialog which corresponds to the marker type. I'm hesitant because I'll have lots of switch case inside the OnActivityResult.
Example - dialog fragments with OnActivityResult
Any other ideas ? Am I missing something ?
I ran into similar problem some time ago and I "hacked" it as follows:
mGoogleMap.setInfoWindowAdapter(new InfoWindowAdapter() {
#Override
public View getInfoWindow(Marker pMarker) {
MarkerDescriptor descriptor = mMarkerDescriptorsMap.get(pMarker);
mGoogleMap.setOnInfoWindowClickListener(descriptor.getOnInfoWindowClickListener(MapActivity.this));
return descriptor.getInfoWindowView();
}
}
MarkerDescriptor should be simple interface that will be implemented for each specific marker type:
public interface MarkerDescriptor {
public View getInfoWindowView();
public OnInfoWindowClickListener getOnInfoWindowClickListener(Context pContext);
}
And to keep the references:
private Map<Marker, MarkerDescriptor> mMarkerDescriptorsMap = new HashMap<Marker, MarkerDescriptor>();
Basics of this idea is that GoogleMap can have only one marker selected at the time, so when user chooses another marker, we change the listeners.
Related
I'm creating a small app that will help a user find a sports club. I'm currently creating a club coordinate variable in the MapsActivity.java file like so:
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
LatLng lansdowneRFC = new LatLng(53.3334103,-6.2201649);
// Adds location to the map (includes a small bit of info about the club)
mMap.addMarker(new MarkerOptions().position(lansdowneRFC).title("Lansdowne RFC").snippet("Aviva Stadium, 62 Lansdowne Rd,"));
}
This produces the following result
I'm trying to add a button to the information box that will bring the user to another activity (The activity will be a sign up form so they can join the club). Considering the approach I'm using is it possible to implement a button or will I have to approach it in another way? I've seen ideas like a custom popup window be suggested for this type of thing but how I implement that into a google maps marker instead of a button is where I'm hitting a brick wall. Any suggestions
You can set your own implementation of InfoWindowAdapter to your GoogleMap object with a call to setInfoWindowAdapter().
And in the adapter you can override getInfoContents() to return whatever view you want.
But the down side is the view that is shown is not a live view, it's an image of the view you created, and you can handle the click on it using a OnInfoWindowClickListener that you set on the GoogleMap object with setOnInfoWindowClickListener().
But that handles the click on the whole window, not just the button.
Another option is to know where your marker is at the screen and show a popup manually above it that has nothing to do with the map, but I would not recommend that approach, with Android's different device sizes and all, that could quickly get very ugly.
Is there a way of calling onClick event of a specific marker manually (without physically tapping the marker)?
No, but you can simulate the onClick event. 2 things happen when you click a marker:
The info window for the corresponding clicked marker is shown.
The camera pans to the marker.
The above can be achieved with 2 lines of code:
marker.showInfoWindow();
map.animateCamera(CameraUpdateFactory.newLatLng(marker.getPosition()), 250, null);
Try this ,
Implement marker click listener from your map class ,
public class MapView extends FragmentActivity implements OnMarkerClickListener{}
it will override onMarkerClickEvent as follows ,
#Override
public boolean onMarkerClick(final Marker marker) {}
NO, you can't triger a marker click event directly (from code).
You can just use mMap.setOnMarkerClickListener(...);, to handle markers click event.
But there is an alternative if you use your map in WebView, so you can trigger a marker click event with JavaScript:
//In V2 version:
GEvent.trigger(markers[i], 'click');
//In V3 version:
google.maps.event.trigger(markers[i], 'click');
The answer is no. You can't set the onClick of a particular marker separately.
However , using Map.setOnMarkerClickListener(_) you can set a listener for all such events. You should be able to retrieve the marker object in the listener called whenever any marker is clicked . You can use some identification to see if this is the particular marker you desire and act accordingly.
The identification could be any of the properties specific to that marker , title being the obvious choice. However, you can filter markers using any desired property.
I just stumbled across this and wasn't helped by the answers. So for future readers - if you are adding a map.setOnMarkerClickListener(yourClickHandler), then it's quite straight forward.
Abstract the logic from yourClickHandler and keep a reference to all the markers... I.e.
private val markers = arrayListOf<Marker>()
Wherever you add your markers to the map, also add them to your markers array. I.e. something like
val marker = MarkerOptions().position(...).......
markers.add(map.addMarker(marker))
And yourClickHandler would look something like
val yourClickHandler = GoogleMap.OnMarkerClickListener {
markerClickHandler(marker = it)
return#OnMarkerClickListener false
}
Now, whenever you press a marker on the map, yourClickHandler will call markerClickHandler() and whatever you do in there will happen. Also, when you wan't to press a marker programmatically, simply pass that marker to markerClickHandler.
You CAN simulate a marker click. Create your MyMarkerManager class extending from MarkerManager class.
The class has a function onMarkerClick() which you can call manually to simulate the event.
For more details refer this link.
https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/MarkerManager.java
The GoogleMap object has a method Marker addMarker(mk: MarkerOptions) that returns a proper Marker instead of MarkerOptions.
So as soon as you add it you can simulate click behavior as follows:
fun addAndZoom(mk: MarkerOptions, needsHighlight: Boolean) {
mapView.getMapAsync { map ->
val actualMarker = map.addMarker(mk)
if(needsHighlight) {
val cameraUpdate = CameraUpdateFactory.newLatLngZoom(mk.position, 14F)
map.animateCamera(cameraUpdate)
actualMarker.showInfoWindow()
}
}
}
I have a map in which I am creating different types of markers. I cannot assign an info window adapter to a marker (gee wouldn't that be nice), I can only assign on InfoWindowAdapter for the entire map (at least I think).
My problem is that I want to show a different type of info widow depending on what I clicked. Id the only way to set one InfoWindowAdapter that will handle creating the correct type of info window based on the marker that I am passed?
Am I missing something easy?
When you add a marker to the map, you are receiving back an ID, which uniquely identifies your marker.
You can create an instance of your InfoWindowAdapter immediately after you add the marker and put it in a map, which keeps the ID as key and your InfoWindowAdapter as value.
Marker marker = map.addMarker(options);
// Create your special infoWindowAdapter for this marker
// ...
adapterMap.put(marker.getId(), youSpecialInfoWindowAdapter);
In the one central InfoWindowAdapter, which you register at the map, you can just use the ID of the marker to get the specific InfoWindowAdapter and delegate to the methods of that.
Access to the map can e.g. be provided in the constructor of the InfoWindowAdapter (to avoid global or static variables):
class CentralInfoWindowAdapter implements InfoWindowAdapter {
Map<String, GoogleMap.InfoWindowAdapter> adapterMap;
public CentralInfoWindowAdapter(
Map<String, GoogleMap.InfoWindowAdapter> adapterMap) {
this.adapterMap = adapterMap;
}
#Override
public View getInfoContents(Marker marker) {
InfoWindowAdapter adapter = adapterMap.get(marker.getId());
return adapter.getInfoContents(marker);
}
#Override
public View getInfoWindow(Marker marker) {
InfoWindowAdapter adapter = adapterMap.get(marker.getId());
return adapter.getInfoWindow(marker);
}
}
You can vary this principle of course. If you have only a few different InfoWindowAdapters depending on the "type" of the marker, you may put an enumeration into the map, which identifies the type of your marker and lets you decide, which kind of real InfoWindowAdapter to use inside the central InfoWindowAdapter, or you may still put instances of your special InfoWindowAdapter into the map, but use the same instance for the same type of marker.
if i am right you want to show a different window adapter on each marker?.. if yes you can add a tag on each marker then inside one of the two infowindow function either infowindow() or infocontents() checks the marker tag and add the appropriate layout.
I mean if i click marker OnMarkerClickListener is called, so the OnMapClickListener did not. Even if i set mMap.setOnMarkerClickListener(null); marker object still masks all click events for underlying map and objects.
How can i set Marker transparent for all user interractions?
This is indeed a "limitation" of markers as of 3.1.59 version of the library.
If you really need them to be markers, please post a feature request on gmaps-api-issues for MarkerOptions.clickable and Marker.setClickable.
If you can, consider using other visual objects, e.g. GroundOverlay. The only problem is they all scale with map, unlike markers. The closest would be Circle with zero radius and 20-50 dp stroke width, but that's only a single color dot.
According to the docs about markers, if you add your own Listener and the onMarkerClick() method returns false, the default behaviour will be executed.
So, in the onMarkerClick() just return true and do nothing else to completely overwrite the default.
The only workaround I found for this issue is to execute the same code in OnMarkerClickListener that you have in OnMapClickListener and return false:
getMap().setOnMarkerClickListener(new OnMarkerClickListener() {
public boolean onMarkerClick(Marker marker) {
onMapClick(marker.getPosition());
return true;
}
});
You can skip setting Marker.Title and in this case the marker won't be clickable. Use Marker.Tag if you need to associate some data (like id or name) with the marker without an ability for end-user to tap and see that.
Make your class implementing Google Maps implement OnMarkerClickListener, i.e.:
public class GoogleMapFragment extends Fragment implements MapImplementation, OnMapReadyCallback,
GoogleMap.OnMapClickListener, GoogleMap.OnMarkerClickListener {
and then implement method inside:
#Override
public boolean onMarkerClick(#NonNull Marker marker) {
// Make Marker not clickable in practice
return true;
}
I've got an Activity that holds a ListFragment on the left and a SupportMapFragment on the right.
List and Map are both backed by the same data. The visual representation of the data on the Map are Markers.
I want to be able to perform a click on either a list item or a Marker and get the corresponding item in the other visual representation.
Restrictions of the framework and my data are:
The Marker class is final and the Marker's id does not have a modificator. This is why I can't use the easiest possible way.
"It's important to not hold on to objects (e.g. Marker) beyond the
view's life. Otherwise it will cause a memory leak as the view cannot
be released." (see SupportMapFragment)
Titles of Markers can occur multiple times. This is why the expensive String comparison is not a way I can go.
Does anyone have a working solution for this issue or can provide a nudge in the right direction?
If you want to be able to register to clicks on a Marker, you'll need to override the InfoWindowAdapter.
You can provide unique information to the title and snippet of a Marker and implement the interface's methods in this style:
#Override
public View getInfoWindow(Marker marker) {
// do nothing here in order to obtain the original info window border.
return null;
}
#Override
public View getInfoContents(Marker marker) {
// Create your own view here. Obtain the unique information stored
// in title / snippet of the current marker.
return createdView;
}
Now you can set the OnInfoWindowClickListener and work with the unique information stored there.
Here's another option. The latest APIs provide an OnMarkerClickListener interface.
https://developers.google.com/maps/documentation/android/marker#marker_click_events