I created a custom OverlayItem class so that I could essentially have one kind of OverlayItem whose Drawable marker would set itself depending on the state of some data that I pass into it.
I have attempted to accomplish this by, on my first attempt, utilizing the setMarker method within the OverlayItem class. Once that did not work I attempt to override the getMarker method and have it return the appropriate marker to represent the data.
Both of these attempts ended with nothing being drawn on the map...however if they are commented out the markers draw just fine (except they of course use the default marker, which isn't what I want).
Here is my code for my custom OverlayItem class (the commented out methods I have tried and they have not worked):
private class MyOverlayItem extends OverlayItem {
private Context mContext;
private MyData mData;
public MyOverlayItem(GeoPoint point, MyData data, Context context) {
super(point, data.getWhat(), data.getWhere());
this.mContext = context;
this.mData = data;
/*if(data.getTemp() > 200)
this.setMarker(res.getDrawable(R.drawable.icon_data_hot_l));
else if(data.getTemp() > 100)
this.setMarker(res.getDrawable(R.drawable.icon_data_neutral_l));
else
this.setMarker(res.getDrawable(R.drawable.icon_data_frozen_l));*/
}
/*#Override
public Drawable getMarker(int stateBitset) {
Resources res = this.mContext.getResources();
if(this.mData.getTemp() > 200)
return res.getDrawable(R.drawable.icon_data_hot_l);
else if(this.mData.getTemp() > 100)
return res.getDrawable(R.drawable.icon_data_neutral_l);
return res.getDrawable(R.drawable.icon_data_frozen_l);
}*/
}
Is there a way to do what I am attempting to do...or do I need to make a unique OverlayItem class corresponding to each state of my data? (Ew.)
I have been able to make the markers appear, however they are appearing upside-down (at least, I think they are, it may just be that their shadow is at the top-left rather than the bottom-right).
All I needed to do was add this line:
this.mMarker.setBounds(0, 0, this.mMarker.getIntrinsicWidth(), this.mMarker.getIntrinsicHeight());
...as well as this line to correctly orient the markers (I added this when my ItemizedOverlay adds tan overlay item:
overlay.setMarker(boundCenterBottom(overlay.getMarker(0)));
The API states that the Marker needs bounds to be displayed...
#celestialorb Either one of your snippets does the job alone (or mine worked with either anyway)
when you use setBounds() alone, you get what you tell it to do... the bounding box for your marker is set. The orientation to the OverlayItem's Point and shadow state of the Marker, etc are set to default (didn't look those up, but they don't do what I want...) :)
mOverlay.setMarker(boundCenterBottom(mOverlay.getMarker(0))); does the complete job for you, setting the bounds and centering the bottom of the Marker on the Point (hence the stylish Method name).
Now, as to best practices, Brighter bulbs than I will have to chime in...
Try overriding the onDraw() method of your overlay class.
Here is another approach:
GoogleMaps: custom ItemizedOverlay and OverlayItem, the correct way to show different marker
I think I prefer this answer though... thanks!
Related
I am using Google Maps and its clustering utility in my app. Clustering itself is working fine, but I have issue, when I try to deal with configuration changes.
This is my case:
User sees a map on a screen, which is a fragment.
When clustering is completed, markers on map appear. User can interact with markers.
When user selects a marker, it is highlighted and BottomSheet is expanded to display.
If user rotates screen (i.e. configuration change happens), I save what marker was selected using onSaveInstanceState (actually I do not save marker itself, but only a link to related List entry like ID). Then, I want to restore that user selection that was before configuration change.
Clustering itself is executed like this:
clusterManager.clearItems();
clusterManager.addItems(eventManager.getEventList());
clusterManager.cluster();
This code is executed, when data is received from server. When clustering is executed, all markers, obviously, are recreated as well. So in order to highlight previous user selection (previous marker), I must know WHEN clustering utility finishes its operation. By that time I am sure that I can safely use such functions like:
clusterManager.getRenderer()).getMarker(<param>)
and
clusterManager.getRenderer()).getClusterItem(<param>)
otherwise, these will return null sometimes, if clustering task is not completed yet.
However, I cannot find a reasonable way, how to get a response from clustering utility (i.e. ClusterManager), when clustering is completed. I think I need to update this standard clustering code:
/**
* Runs the clustering algorithm in a background thread, then re-paints when results come back.
*/
private class ClusterTask extends AsyncTask<Float, Void, Set<? extends Cluster<T>>> {
#Override
protected Set<? extends Cluster<T>> doInBackground(Float... zoom) {
mAlgorithmLock.readLock().lock();
try {
return mAlgorithm.getClusters(zoom[0]);
} finally {
mAlgorithmLock.readLock().unlock();
}
}
#Override
protected void onPostExecute(Set<? extends Cluster<T>> clusters) {
mRenderer.onClustersChanged(clusters);
}
}
I think, that onPostExecute should provide a response not to Renderer only, but to a user of ClusterManager (like my fragment) that clustering is done. But I do not want to modify standard code.
Is there a better way how to handle this case?
You are going about this the wrong way.
You should implement:
class YourClusterItemRenderer extends DefaultClusterRenderer<YourClusterItem>
and set it to ClusterManager as renderer like this:
mClusterManager.setRenderer(new YourClusterItemRenderer(...));
Override YourClusterItemRenderer's methods onBeforeClusterItemRendered and onBeforeClusterRendered and there you can change what each marker looks like according to your logic as per parameters from your server.
So when ClusterManager finishes rendering all your markers already look like you want them according to user state etc.
my problem is as follows.
I am creating multiple itemized overlays. (because every overlay gets a different drawable)
I customized the itemized overlay class, but when i add it to the mapview overlays, the class is transformed into an overlay class.
to make it worse i got 3 classes creating overlays on the same map. each class represents an item on the map with it's own intelligence behind it.
the problem i now have is that i want to remove an overlay, but i can not be sure that the index i inserted it on, is also the index it has when i try to remove it. (the other classes might have inserted an overlay in the mean time)
the classes are self updating, so i do not want a solution that fires an update or delete event from the main class. (the whole point is to add a class and forget about it)
so my question would be: how can i identify which layer is which when i want to call a remove on that layer. i think the information is available, but i do not know how to get to it.
this is the code i am using to add the overlay
OverlayItem overlayitem = new OverlayItem(p,myNaam ,myOmschrijving );
LocationOverlay = new MyLocationOverlay(drawable, myContext);
LocationOverlay.SetLocation(i,overlayitem);
myOverlays.add(LocationOverlay);
You can set a certain integer as a position for every overlays
something like that :
mapView.getOverlays().add(0,myScaleBarOverlay);
and when you want ro remove this call:
mapView.getOverlays().remove(0);
mapView.invalidate();
Regard
You don't have to remove specific layer. You can remove an overlay specified by it's reference (e.g. myOverlay).
LocationOverlay myOverlay = new MyLocationOverlay(drawable, myContext); //`you forgot the name of variable`
mapView.getOverlays().remove(myOverlay);
I currently have an Android Mapview that displays a very basic subclass of OverlayItem with some custom properties:
public class StreetLocation extends OverlayItem{
public int id;
public float latitude, longitude;
public float distance;
boolean isHouse = false;
boolean hasSnacks = false;
... and so on, with a bunch of getters/setters
}
These are retrieved from a database, via building a co-ordinate span region (based on the user's view of the map) with no problems at all.
Now I have added a few simple Button's on my UI which I wish to use to toggle the display of markers with isHouse and / or hasSnacks. Currently the only way I can think of doing this is by completely clearing the MapView of all markers, then adding only the ones I want back on to the MapView, by filtering the array of StreetLocation's I get back from my database for the users view.
My issue with this is that this seems like a lot of work - is there any other way to simply hide or toggle the visual representation of the marker without clearing the entire map, since all markers are retrieved initially regardless of properties?
My apologies if this was a bit long, and I'll be glad to explain more / post code if required.
I need to check if the data of a newly created OverlayItem is already existing on the list of OverlayItems already displayed on the map. I made a code to check if the data of the OverlayItem is already existing but I'm encountering an error on it. How can I extract an OverlayItem from an Overlay?
My current code is like this:
//where mapOverlays = mapView.getOverlays() and overlayItem is the newly created overlayItem
public boolean isExisting(List<Overlays> mapOverlays, OverlayItem overlayItem)
{
ItemizedOverlay overlay;
OverlayItem itemToCompare;
for(int i = 0; i < mapOverlays.size(); i++)
{
overlay = (ItemizedOverlay)mapOverlays; //I am getting an error here: java.util.Collections$SynchronizedRandomAccessList (from e.getMessage()). The stack trace does not contain any specific exception but only the trace of the error pointing to this line.
existingOverlayItem = overlay.getItem(i);
if(itemToCompare.getPoint().equals(overlayItem.getPoint())
&& itemToCompare.getSnippet().equals(overlayItem.getSnippet())
&& itemToCompare.getTitle().equals(overlayItem.getTitle()))
return true; //if all data are the same
}
return false;
}
i don't know MapOverlay, but you probably can"t put List into a MapOverlay
That code looks a bit weird. Where is coming itemToCompare? I can't see it declared anywhere. If itemToCompare is the same Class as overlayItem, why not override equals in the class and use it instead of comparing every parameter?
Even better, overriding equals in the Class will grant you access to:
mapOverlays.contains(overlayItem);
So your entire isExisting method will be replaced by the call above. You can see more information on how equals work here.
Thanks all for answering my question. I found out that my problem happens because I'm casting mapOverlays to a wrong class.
Also, instead of extracting OverlayItem from mapOverlays (which I can't find out how), I am just making the validation inside addOverlay(OverlayItem overlayItem). I can't do this before because I am always reinitializing the value of my itemizedOverlay. As such, I can't compare the newly added OverlayItem to the old ones because itemizedOverlay does not hold the old values anymore. What I did is instead of always reinitializing itemizedOverlay, I am just always clearing mapOverlays which equals to mapView.getOverlays().clear().
I tried searching the forums on this one, but I wasn't able to find anything on my problem.
To describe my problem, everytime my location changes, it redraws the center maker on the map.... Only catch is that it doesn't delete the previous one. I can get it to delete the previous one when the location is changed, but I have no idea how to pass the original overlay in-between classes.
Also, pastebin here
Thanks in advance,
hwrd
You need to clear "existing" items in the Overlay list before adding new ones.
public void createOverlay(GeoPoint point, MapView mv)
{
//Make overlay reference declaration
List mapOverlays = mv.getOverlays();
Drawable drawable = this.getResources().getDrawable(R.drawable.center_marker);
FindScreenOverlays itemizedoverlay = new FindScreenOverlays(drawable);
OverlayItem overlayitem = new OverlayItem(point,null,null);
//clear your list before adding new overlays unless you want to see all the previous locations as well.
itemizedoverlay.addOverlay(overlayitem);
mapOverlays.add(itemizedoverlay);
}
Adding an OverlayItem is similar to adding an overlay. Just extend ItemizedOverlay. ( public class YourItemizedOverlay extends ItemizedOverlay )