I have a onMarkerClickListener on my google map which fires when a marker is pressed as it should.
the markers are created in a Tag class that creates the marker within itself on a map that is passed though:
instance of the list at the start of the map activity:
//tags
List<Tag> tags = new ArrayList<Tag>();
In the onCreate() of the activity that contains the googleMap I add the markers to a list:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....other code here
//add the markers to the map
tags.add(new Tag(map,1,"City Of Dundee", "Home Of The Jakey", DUNDEE, this.getResources(),R.drawable.ic_launcher));
tags.add(new Tag(map,2,"Some Place","This is some place",LOCATION_Z,this.getResources(),R.drawable.ic_launcher));
....other code here
}//end on create
Constructor for the Tag class:
public Tag(GoogleMap map, int atagID,String tagTitle, String tagSnippet, LatLng tagPosition, Resources resource, int id){
this.tagID = atagID;
this.position = tagPosition;
this.title = tagTitle;
this.snippet = tagSnippet;
this.icon = BitmapDescriptorFactory.fromResource(id);
this.theTag = map.addMarker(new MarkerOptions()
.position(tagPosition)
.title(tagTitle)
.snippet(tagSnippet)
.icon(icon));
}
This creates the tag and it display on the map properly
In the listener for the onMarkerClickedListener i compare the marker clicked on the map to the marker from the list but the if statement never passes, even when I compare the titles which are identical.
The Listener:
onMarkerClickListener = new OnMarkerClickListener(){
#Override
public boolean onMarkerClick(Marker marker) {
//for loop that goes over array or marker
//if marker is equal to mark in array
//do marker functionality
for(Tag currentTag : tags){
if(currentTag.theTag == marker){
switch(currentTag.tagID){
case 1:
//do something for that button
Toast.makeText(getApplicationContext(), "marker 1", Toast.LENGTH_SHORT).show();
return true;
case 2:
Toast.makeText(getApplicationContext(), "marker 2", Toast.LENGTH_SHORT).show();
return false;
default:
Toast.makeText(getApplicationContext(), "default", Toast.LENGTH_SHORT).show();
return false;
}
}else{
Toast.makeText(getApplicationContext(), "theTag " + currentTag.tagID + ": " + currentTag.theTag.getTitle(), Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(), "marker: " + marker.getTitle(), Toast.LENGTH_SHORT).show();
}
}
return false;
}
};
I hove no idea why it never reaches the switch statement any ideas?
I found the documentation i was looking for, don't know how i missed it.
As I have read you can't compare a marker using the '==' but you can using
if(markerA.equals(markerB)
{
}
Like so:
if(theTag.equals(marker){
//it will compare properly this way instead of returning false every time
}
Reference to website:
https://developers.google.com/maps/documentation/android/marker
You do not need the for loop in your code. When you do the equality test on the markers(using ==) it will return false, and never reach your switch. Instead try removing the for loop and just us.
switch(marker.getId()){
case 1:
//do something for that button
Toast.makeText(getApplicationContext(), "marker 1", Toast.LENGTH_SHORT).show();
return true;
case 2:
Toast.makeText(getApplicationContext(), "marker 2", Toast.LENGTH_SHORT).show();
return false;
default:
Toast.makeText(getApplicationContext(), "default", Toast.LENGTH_SHORT).show();
return false;
}
}else{
Toast.makeText(getApplicationContext(), "theTag " + currentTag.tagID + ": " + currentTag.theTag.getTitle(), Toast.LENGTH_SHORT).show();
Toast.makeText(getApplicationContext(), "marker: " + marker.getTitle(), Toast.LENGTH_SHORT).show();
}
Expanding on #MaciejGórski comment from the selected answer I would suggest using the ID of the marker (which is oddly a string).
This helps you avoid keeping hard references to Markers which themselves have references to the GoogleMap object, and thus the entire MapView/MapFragment/SupportMapFragment
In my class I have:
private Map< String, MyObject > markersMap = new HashMap< String, MyObject >();
Then when I create the markers I add them to the map:
markersMap.put( marker.getId(), myObj );
Then in the listener I am able to retrieve myObj like this
#Override
public void onInfoWindowClick( Marker m )
{
Log.v( TAG, "onInfoWindowClick m.getId = " + m.getId() );
MyObject myObj = markersMap.get( m.getId() );
Log.v( TAG, "onInfoWindowClick myObj = " + myObj );
}
This way I avoid leaking the Views/Activity/Map etc
The == operator will compare the direction of memory of both objects (which are not the same, since are different instances) instead of the object properties.
the equals method, will compare the value of the attributes of your object or if you override the equals you can compare what ever you want, which will be more suitable to you.
Related
How can send values when user click the mMap.setOnMarkerClickListener ?
I don't know why I always get same value when user click the google map marker.
this is firebase data
"Campsite" : {
"-MfS3VgBGROs_afkVrjl" : {
"CamperSiteAddress" : "Charles St & Esplanade W, Triabunna TAS 7190, Australia",
"CamperSiteID" : "-MfS3VgBGROs_afkVrjl",
"Counrty" : "Australia",
"CamperSiteImages" : [
"https://firebasestorage.googleapis.com/v0/b/campingau-6b84d.appspot.com/o/CampSitePhotos%2F1627206575487.null?alt=media&token=bff07bd4-fd17-4099-a0d7-db0418311425",
"https://firebasestorage.googleapis.com/v0/b/campingau-6b84d.appspot.com/o/CampSitePhotos%2F1627206580478.null?alt=media&token=995bf983-0ee8-4f25-af3f-921aa5b0077c",
"https://firebasestorage.googleapis.com/v0/b/campingau-6b84d.appspot.com/o/CampSitePhotos%2F1627206584072.null?alt=media&token=137b87a9-f6fb-4f75-9d53-7c3efda8cda4"
],
"CamperSiteLatitude" : -42.508695,
"CamperSiteLongitude" : 147.916617,
"CamperSiteName" : "Encampment Cove Walk"
}
And this is my code
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Campsite");
Query query = reference.orderByChild("country").equalTo(country);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String previousChildName) {
list.clear();
List<String> mapImage = (List<String>) dataSnapshot.child("CamperSiteImages").getValue();
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
Double lat = (Double)map.get("CamperSiteLatitude");
Double lng = (Double)map.get("CamperSiteLongitude");
LatLng newLocation = new LatLng(lat, lng);
String CampSiteName = (String) map.get("CamperSiteName");
String CampSitePostID = (String) map.get("CamperSiteID");
String CampSiteType= (String) map.get("type");
String CampSiteImage = mapImage.get(0);
list.add(String.valueOf(newLocation));
for(int i = 0; i < list.size(); i++){
try{
//bitmap = Ion.with(getApplicationContext()).load(CampSiteImage).withBitmap().fitCenter().resize(120,120).asBitmap().get();
} catch (Exception e){
e.printStackTrace();
}
mMap.addMarker(new MarkerOptions().position(newLocation).title(CampSiteName).icon(BitmapDescriptorFactory.fromResource(icon)));
mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
Toast.makeText(getContext(),CampSitePostID, Toast.LENGTH_SHORT).show();
return false;
}
});
}
progressDialog.dismiss();
}
Now when I click the marker the title is correct, but I always get same CamperSiteID. How can solve this problem?
When you add a marker it returns a Marker object which has a field available for the application to associate with the marker - called the tag field (accessed using getTag and setTag). This tag field can then be retrieved in the onMarkerClick callback.
So in your code you could do this:
Marker m = mMap.addMarker(new MarkerOptions().position(newLocation).title(CampSiteName).icon(BitmapDescriptorFactory.fromResource(icon)));
m.setTag(CampSitePostID);
and in the marker click callback:
public boolean onMarkerClick(Marker marker) {
String campSiteID = "?";
if (marker.getTag() != null) {
campSiteID = (String) marker.getTag();
}
//...use campSiteID instead of CampSitePostID
}
Here's more info from the documentation: https://developers.google.com/maps/documentation/android-sdk/marker#associate_data_with_a_marker.
If your application has more data to associate than a simple String then more complex solutions can be implemented:
Since the tag is an object instance you can associate any object of your choosing such as a class with many properties (or even the map variable in your example)
Since each marker has a unique id field (m.getId()) you can use that field as a key into your own map implementation to access associated data. Marker ids are unique for the current instance of the map (the numbering is reset when the app is restarted or the map is reinitialized).
As combination of (1) and (2) you can create your own mapping key and set that as the tag value and use that key to map into your own data.
Notes:
The map has only one marker click event handler. In your code you are setting the marker click event handler on every firebase callback. This is unnecessary and this leads to the likely result where the last marker added is the CampSitePostId used for any marker click.
In your onChildAdded you first list.clear() which empties the list and then perform one list.add and then loop on the size of the list; but it will always be of size 1.
i'm trying to place markers with custom icon and colors. Currently working in kotlin for android. I am trying to set markers on click listener as stated in docs of google. But on click nothing is happening.
overidden the function onMarkerClick and tried to get tag of that marker as i stored in that and before that i treid to use .equal method to test that if the marker is equal to that event marker
override fun onMarkerClick(marker: Marker): Boolean {
// Retrieve the data from the marker.
var clickCount = marker.tag as Int?
// Check if a click count was set, then display the click count.
if (clickCount != null) {
clickCount = clickCount!! + 1
marker.tag = clickCount
Toast.makeText(
this,
marker.title +
" has been clicked " + clickCount + " times.",
Toast.LENGTH_SHORT
).show()
}
// Return false to indicate that we have not consumed the event and that we wish
// for the default behavior to occur (which is for the camera to move such that the
// marker is centered and for the marker's info window to open, if it has one).
return false
}
mMap = googleMap
val streetRight = mMap.addMarker(
MarkerOptions().position(
LatLng(
-xx.xxxxx,
xxx.xxxxxx
)
).icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_street_view_person))
)
val streeTop = mMap.addMarker(
MarkerOptions().position(
LatLng(
-xx.xxxxx,
xxx.xxxxxx
)
).icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_street_view_person))
)
streeTop.tag = 1
streetRight.tag = 2
I expect that when i click on marker it should show a toast that marker is clicked. But nothing happens just map centers that marker.
I was able to get your code working, but I added the listener to onMapReady:
override fun onMapReady(gMap: GoogleMap?) {
gMap?.let {
it.setOnMarkerClickListener {
var clickCount = it.tag as Int?
// Check if a click count was set, then display the click count.
if (clickCount != null) {
clickCount = clickCount!! + 1
it.tag = clickCount
Toast.makeText(
this#MapFragment.context,
it.title +
" has been clicked " + clickCount + " times.",
Toast.LENGTH_SHORT
).show()
}
// Return false to indicate that we have not consumed the event and that we wish
// for the default behavior to occur (which is for the camera to move such that the
// marker is centered and for the marker's info window to open, if it has one).
false
}
}
}
This gets called after you call .getMapAsync()
I write a code for showing map marker in maps activity. When I want to search some array value in EditText and I input wrong value I want to show error message here is my btncari.onClickListener
btncari.setOnClickListener(new View.OnClickListener() {
int duration = Toast.LENGTH_SHORT;
#Override
public void onClick(View view) {
if (editcari.getText().toString().isEmpty() || editcari.getText().toString().equals(null)
) {
Toast.makeText(FragmentPeta.this, "Masukkan Nama Wisata", duration).show();
} else {
for(int i=0; i < nama.length; i++) {
if (nama[i].toLowerCase().contains(editcari.getText().toString())){
marker.remove();
myMap.addMarker(new MarkerOptions().position(new LatLng(lat[i],lon[i])).title(nama[i]).icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_wisata)));
CameraPosition campos = new CameraPosition.Builder()
.target(new LatLng(lat[i],lon[i]))
.zoom(18)
.build();
CameraUpdate camUpd3 = CameraUpdateFactory.newCameraPosition(campos);
myMap.animateCamera(camUpd3);
break;
}
}
}
}
});
i have try to handle some error like if (editcari.getText().toString().contains("array value")) but it doesn't work. also i've tried with Arrays like this
if (Arrays.asList(nama).contains(editcari.getText().toString())){
Toast.makeText(FragmentPeta.this, "welcome", duration).show();
}
else
{
Toast.makeText(FragmentPeta.this, "Error", duration).show();
}
the difference is when I use Arrays it will always show "Error"eventhough I input the right value from array. So when I put right value on editsearch it will zoom to marker that I typed. the problem is the "error message" is still keep showing.
I put if Arrays.asList above the if (editcari.getText())
When using arrays yourlooking at a specific index of the array, not the entire array. Using a List on the other hand youcan call `list.contains(value)' to parse through the entire list to verfiy if the value is within the contents of such list.
if(!list.contains(value){
//show error related msg to user
}
I am using the following to show a corresponding marker image based on the returned business name using Google Places and Maps:
if (name.contains("walmart")) {
mIcon = R.drawable.ic_wm_poi;
} if (name.contains("speedco")) {
mIcon = R.drawable.ic_poi_speedco;
}
The logcat shows "Unkown icon: business name" which does correspond with one of my if statements but doesn't show the corresponding icon. I've tried using if/else and switch statements. I've also tried name.contains, name.equals and name.contentEquals. For example, if I search "Walmart", the response returns the business name "Walmart" as shown in the logcat, and the marker shows the name "Walmart" once clicked yet the corresponding icon doesn't show. I am setting the marker icon like so:
markerOptions.icon(BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
EDIT
After implementing crickets' comment, the icons for the various locations still don't show. This is what I have:
public class MarkerIcons {
public static HashMap<String, Integer> poiIcons;
public static HashMap<String, Integer> getpoiIcons(){
poiIcons = new HashMap<>();
poiIcons.put("Walmart", R.drawable.ic_wm_poi);
poiIcons.put("Walmart Supercenter", R.drawable.ic_wm_poi);
poiIcons.put("Walmart Neighborhood Market", R.drawable.ic_wm_poi);
return poiIcons;
}
public static Integer markerIcon(String name){
String TAG_MI = "marker";
Log.d(TAG_MI, "Loc Name: " + name);
int mIcon = 0;
if(getpoiIcons().containsKey(name)){
mIcon = getpoiIcons().get(name).intValue();
return mIcon;
}else{
mIcon = R.drawable.default_marker;
return mIcon;
}
}
}
How I am calling these functions:
Get Place Class
public class GetNearbyBusinessData extends AsyncTask<Object, String, String> {
private String TAG = "getplace";
private String googlePlacesData;
private GoogleMap mMap;
private String url;
#Override
protected String doInBackground(Object... params) {
try {
Log.d(TAG, "doInBackground entered");
mMap = (GoogleMap) params[0];
url = (String) params[1];
DownloadUrl downloadUrl = new DownloadUrl();
googlePlacesData = downloadUrl.readUrl(url);
Log.d(TAG, "doInBackground Exit");
} catch (Exception e) {
Log.d(TAG, e.toString());
}
return googlePlacesData;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "onPostExecute Entered");
List<HashMap<String, String>> nearbyPlacesList;
DataParser dataParser = new DataParser();
nearbyPlacesList = dataParser.parse(result);
ShowNearbyBusiness(nearbyPlacesList);
Log.d(TAG, "onPostExecute Exit");
Log.d(TAG, result);
}
private void ShowNearbyBusiness(List<HashMap<String, String>> nearbyPlacesList) {
for (int i = 0; i < nearbyPlacesList.size(); i++) {
Log.d(TAG,"Entered into showing business");
MarkerOptions markerOptions = new MarkerOptions();
HashMap<String, String> googlePlace = nearbyPlacesList.get(i);
double lat = Double.parseDouble(googlePlace.get("lat"));
double lng = Double.parseDouble(googlePlace.get("lng"));
String placeName = googlePlace.get("place_name");
String vicinity = googlePlace.get("vicinity");
LatLng latLng = new LatLng(lat, lng);
markerOptions.position(latLng);
markerOptions.title(placeName + "-" + vicinity);
markerOptions.icon(BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
mMap.addMarker(markerOptions);
//move map camera
mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
mMap.animateCamera(CameraUpdateFactory.zoomTo(11));
}
}
}
EDIT 2
After setting break points for the markerIcon method and the line that calls the markerIcon method, the debug log shows that the returned response string is in fact "Walmart". Which is matching the key value in the HashMap. The returned icon for the marker is being returned as "0" though. It isn't returning the "value" from the hash map. I am setting the marker icon using this line:
mIcon = getpoiIcons().get(name).intValue();
Given the options available, I assumed that getting the matching "key" (in this case the string "name") would return its int "value". Am I missing something?
Even though the logcat shows the returned business names of Walmart, Walmart Supercenter and Walmart Neighborhood Market, it still shows the default marker. I have no idea why it is not showing the correct marker icon. Also, if it helps, my marker icons are vector images.
I think every thing work fine. but the error is, so far i have seen your code is,
markerOptions.icon(BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
what you want to do is,
BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(markerIcon(placeName)));
markerOptions.icon(icon);
Hope it helps.
U have function in separate class try this
MarkerIcons.markerIcon(placeName))
or update me if ur problem is cleared.
After trying the answers that were given and attempting to debug my code to find what was wrong, I found the error.
For anyone else that may have the same issue of the marker icons not showing, through trial and error I had found that you cannot use vector images that are in xml format as your markers. Once I switched them all over to png files, every icon showed without flaw.
Hope this answer helps someone else in the future. Thank you everyone for your contributions.
From what I have read so far, it seems that there is no way to style Google Maps via the Android API.
Has anyone seen differently or know a way to style Google Maps on Android (change feature colors, etc.)?
From what I have seen, the only alternative to a full map library for Android (or iOS) is Mapbox, but their Android library is still under heavy development.
Google introduced Cloud-based Map Styling (however it is in Beta now). Using this feature, it is possible to set up a Map Id and a Map Style in Google Cloud Platform and to use it in the Android App (the only thing you need to do in the app is to specify the Map Id).
Here are the details:
https://developers.google.com/maps/documentation/android-sdk/cloud-based-map-styling
PS Here is the description of the 'old' option where you need to create a special json file and use it in the Android app to get a styled map:
https://developers.google.com/maps/documentation/android-sdk/styling
We can create styled maps as follows in android:
StyledMapDemoActivity.java
public class StyledMapDemoActivity extends AppCompatActivity implements OnMapReadyCallback {
private GoogleMap mMap = null;
private static final String TAG = StyledMapDemoActivity.class.getSimpleName();
private static final String SELECTED_STYLE = "selected_style";
// Stores the ID of the currently selected style, so that we can re-apply it when
// the activity restores state, for example when the device changes orientation.
private int mSelectedStyleId = R.string.style_label_default;
// These are simply the string resource IDs for each of the style names. We use them
// as identifiers when choosing which style to apply.
private int mStyleIds[] = {
R.string.style_label_retro,
R.string.style_label_night,
R.string.style_label_grayscale,
R.string.style_label_no_pois_no_transit,
R.string.style_label_default,
};
private static final LatLng SYDNEY = new LatLng(-33.8688, 151.2093);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mSelectedStyleId = savedInstanceState.getInt(SELECTED_STYLE);
}
setContentView(R.layout.styled_map_demo);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onSaveInstanceState(Bundle outState) {
// Store the selected map style, so we can assign it when the activity resumes.
outState.putInt(SELECTED_STYLE, mSelectedStyleId);
super.onSaveInstanceState(outState);
}
#Override
public void onMapReady(GoogleMap map) {
mMap = map;
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 14));
setSelectedStyle();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.styled_map, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_style_choose) {
showStylesDialog();
}
return true;
}
/**
* Shows a dialog listing the styles to choose from, and applies the selected
* style when chosen.
*/
private void showStylesDialog() {
// mStyleIds stores each style's resource ID, and we extract the names here, rather
// than using an XML array resource which AlertDialog.Builder.setItems() can also
// accept. We do this since using an array resource would mean we would not have
// constant values we can switch/case on, when choosing which style to apply.
List<String> styleNames = new ArrayList<>();
for (int style : mStyleIds) {
styleNames.add(getString(style));
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.style_choose));
builder.setItems(styleNames.toArray(new CharSequence[styleNames.size()]),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
mSelectedStyleId = mStyleIds[which];
String msg = getString(R.string.style_set_to, getString(mSelectedStyleId));
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
Log.d(TAG, msg);
setSelectedStyle();
}
});
builder.show();
}
/**
* Creates a {#link MapStyleOptions} object via loadRawResourceStyle() (or via the
* constructor with a JSON String), then sets it on the {#link GoogleMap} instance,
* via the setMapStyle() method.
*/
private void setSelectedStyle() {
MapStyleOptions style;
switch (mSelectedStyleId) {
case R.string.style_label_retro:
// Sets the retro style via raw resource JSON.
style = MapStyleOptions.loadRawResourceStyle(this, R.raw.mapstyle_retro);
break;
case R.string.style_label_night:
// Sets the night style via raw resource JSON.
style = MapStyleOptions.loadRawResourceStyle(this, R.raw.mapstyle_night);
break;
case R.string.style_label_grayscale:
// Sets the grayscale style via raw resource JSON.
style = MapStyleOptions.loadRawResourceStyle(this, R.raw.mapstyle_grayscale);
break;
case R.string.style_label_no_pois_no_transit:
// Sets the no POIs or transit style via JSON string.
style = new MapStyleOptions("[" +
" {" +
" \"featureType\":\"poi.business\"," +
" \"elementType\":\"all\"," +
" \"stylers\":[" +
" {" +
" \"visibility\":\"off\"" +
" }" +
" ]" +
" }," +
" {" +
" \"featureType\":\"transit\"," +
" \"elementType\":\"all\"," +
" \"stylers\":[" +
" {" +
" \"visibility\":\"off\"" +
" }" +
" ]" +
" }" +
"]");
break;
case R.string.style_label_default:
// Removes previously set style, by setting it to null.
style = null;
break;
default:
return;
}
mMap.setMapStyle(style);
}
}
StyledMapDemoActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />