My app have approximately 1,500 markers on a map that are being shown through clusters so as not to overload the application. these bookmarks are currently shown as BitmapDescriptorFactory.defaultMarker ()
However, when I modify the code for each dot to show a custom bitmap with values on the markers, only a few devices have this error, among them LG K10 LTE and some Motorolas. Most appliances work normally.
When i use this function, before i finish rendering all 1500 markers, it crashes with the following error:
"Could not allocate dup blob fd."
In research on this error, it seems to me that this is a memory overflow and that I should store these markers in LRU cache, but I am not able to do this in conjunction with the clusters.
Has anyone had this or did you have an idea / suggestion to solve this problem?
The following is the bitmaps renderer code snippet:
public class OwnRendring extends DefaultClusterRenderer<MyItem> {
OwnRendring(Context context, GoogleMap map, ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
markerOptions.snippet(item.getSnippet());
markerOptions.title(item.getTitle());
markerOptions.anchor(0.33f, 1f);
markerOptions.infoWindowAnchor(0.33f,0f);
int cor = (item.getPublico() ? cfgCorPostoPublico : cfgCorPostoPrivado);
String preço = item.getTitle().substring(item.getTitle().length() - 5);
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(createMarker(preço, cor)));
super.onBeforeClusterItemRendered(item, markerOptions);
}
protected boolean shouldRenderAsCluster(Cluster cluster) {
return cfgCluster && cluster.getSize() >= cfgClusterMin;
}
}
#Override
public void onCameraIdle() {mClusterManager.cluster();}
private Bitmap createMarker(String text, int color) {
View markerLayout = getLayoutInflater().inflate(R.layout.custom_marker, null);
ImageView markerImage = markerLayout.findViewById(R.id.marker_image);
TextView markerRating = markerLayout.findViewById(R.id.marker_text);
markerImage.setImageResource(R.drawable.pin_shadow);
markerImage.clearColorFilter();
markerImage.getDrawable().mutate().setColorFilter(color, PorterDuff.Mode.MULTIPLY );
markerRating.setText(text);
markerLayout.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
markerLayout.layout(0, 0, markerLayout.getMeasuredWidth(), markerLayout.getMeasuredHeight());
final Bitmap bitmap = Bitmap.createBitmap(
markerLayout.getMeasuredWidth(),
markerLayout.getMeasuredHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
markerLayout.draw(canvas);
return bitmap;
}
Alternatively, I solved my problem by reducing the markers rendering as follows:
I modify the code to force cluster all markers, except those that are visible on the screen.
For this I had to import and modify the original code of the maps-utils library because the rendering only happened after zoom-in or zoom-out, not rendering after the map move.
public class OwnRendring extends DefaultClusterRenderer<MyItem> {
OwnRendring(Context context, GoogleMap map, ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
... original code ...
}
protected boolean shouldRenderAsCluster(Cluster cluster) {
boolean isInBounds = latLngBounds.contains(cluster.getPosition());
return !isInBounds || (cfgCluster && cluster.getSize() >= cfgClusterMin);
}
}
#Override
public void onCameraIdle() {
mClusterManager.cluster();
latLngBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
}
and in the library maps-utils (class DefaultClusterRenderer) i commented those lines, because they returns without render clusters when user moves the map:
#SuppressLint("NewApi")
public void run() {
// if (clusters.equals(DefaultClusterRenderer.this.mClusters)) {
// mCallback.run();
// return;
// }
... original code ...
}
Obviously this is not the right answer to my question, but it can be a valid alternative for anyone who is having this problem, so far without a conclusive answer.
*** Please someone grammatically correct my answer, because English is not my native language.
Related
I wrote the following method to display my markers (clustered). However, some of the clusters work fine - i.e. when I zoom in, the blue cluster icon (that says 10+ for ex) goes away and reveals the individual markers.
However, some times, when I zoom in fully, the blue cluster marker is still displayed on full zoom ALONG with one of the individual markers. Why is this happening?
private void displayCluster(List<MyItem> items) {
mClusterManager = new ClusterManager<MyItem>(this, googleMap);
mClusterManager.setRenderer(new MyRender(getApplicationContext(),googleMap,mClusterManager));
googleMap.setOnCameraChangeListener(mClusterManager);
googleMap.setOnMarkerClickListener(mClusterManager);
for (MyItem item : items) {
mClusterManager.addItem(item);
}
}
DefaultClusterRenderer decides whether the markers should be clustered or not.This class contains DefaultClusterRenderer#shouldRenderAsCluster() method - in which the clustering starts only when size of cluster is > MIN_CLUSTER_SIZE. Default value of MIN_CLUSTER_SIZE is 4.
You need to extend DefaultClusterRenderer class and override shouldRenderAsCluster() method to provide your own logic:
class CustomRenderer<T extends ClusterItem> extends DefaultClusterRenderer<T>
{
public CustomRenderer(Context context, GoogleMap map, ClusterManager<T> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected boolean shouldRenderAsCluster(Cluster<T> cluster) {
//start clustering if at least 2 items overlap
//Change your logic here
return cluster.getSize() > 1;
}
}
private void setUpMapIfNeeded() {
if (mMap != null) {
return;
}
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
if (mMap != null) {
mClusterManager = new ClusterManager<Place>(this, mMap);
mClusterManager.setRenderer(new CustomRenderer<Place>(this, mMap, mClusterManager));
........
}
}
In this scenario I want to draw a bitmap on Google Maps using gms v2 and each user position update enforces bitmap update. Currently I use following code snippet:
public void init(){
result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
canvas = new Canvas(result);
}
public void update(){
// draw on canvas ...
draw(result);
}
public void draw(Bitmap modifiedBmp) {
if (overlay != null) {
overlay.remove();
}
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromBitmap(modifiedBmp);
overlay = map.addGroundOverlay(new GroundOverlayOptions().image(descriptor).positionFromBounds(bounds).zIndex(100));
}
The update() method is called each second. I find this approach extremely inefficient and I'm searching for a better solution (i.e. that doesn't require to add/remove overlay after each update). Drawing primitives on map using addPolygon(...) and addPolyline(...) isn't an option because I require drawing capabilities not present in standard api.
One optimization could be to check if the new position is the same as the old one and don't redraw if that is the case. Also I don't think that the descriptor need to be created each time.
Another approach for moving markers is described here. It's the one from the official sample.
I'm not sure if this is what you want, but this is how I used custom bitmaps in Google Maps.
The marker code:
BitmapDescriptor iconBitmap = BitmapDescriptorFactory
.fromResource(R.drawable.item_map_marker);
MarkerOptions options = new MarkerOptions();
options.position(new LatLng(hs.lat, hs.lng));
options.title(hs.sitename);
options.snippet(hs.street + ", " + hs.suburb);
options.icon(iconBitmap);
mMap.addMarker(options);
The tooltip adapter:
public class MyInfoWindowAdapter implements InfoWindowAdapter {
public interface OnRenderCustomInfoWindow {
public void onRender(Marker marker, View mWindow);
}
private View mWindow;
private OnRenderCustomInfoWindow mRenderer;
public MyInfoWindowAdapter(Context context,
OnRenderCustomInfoWindow onRender) {
mRenderer = onRender;
mWindow = LayoutInflater.from(context).inflate(
R.layout.view_services_map_infowindow, null);
}
#Override
public View getInfoWindow(Marker marker) {
mRenderer.onRender(marker, mWindow);
return mWindow;
}
#Override
public View getInfoContents(Marker marker) {
return null;
}
}
I'm trying to apply a visual effect to a viewgroup. My idea is to grab a bitmap of the viewgroup, shrink it down, expand it back up, and draw it over the viewgroup to give it a blocky, low quality effect.
I've got most of the way there using this code:
public class Blocker {
private static final float RESAMPLE_QUALITY = 0.66f; // less than 1, lower = worse quality
public static void block(Canvas canvas, Bitmap bitmap_old) {
block(canvas, bitmap_old, RESAMPLE_QUALITY);
}
public static void block(Canvas canvas, Bitmap bitmap_old, float quality) {
Bitmap bitmap_new = Bitmap.createScaledBitmap(bitmap_old, Math.round(bitmap_old.getWidth() * RESAMPLE_QUALITY), Math.round(bitmap_old.getHeight() * RESAMPLE_QUALITY), true);
Rect from = new Rect(0, 0, bitmap_new.getWidth(), bitmap_new.getHeight());
RectF to = new RectF(0, 0, bitmap_old.getWidth(), bitmap_old.getHeight());
canvas.drawBitmap(bitmap_new, from, to, null);
}
}
I simply pass in the canvas to draw on and a bitmap of what needs to be scaled down+up and it works well.
public class BlockedLinearLayout extends LinearLayout {
private static final String TAG = BlockedLinearLayout.class.getSimpleName();
public BlockedLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
applyCustomAttributes(context, attrs);
setup();
}
public BlockedLinearLayout(Context context) {
super(context);
setup();
}
private void setup() {
this.setDrawingCacheEnabled(true);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// block(canvas); If I call this here, it works but no updates
}
#Override
public void onDraw(Canvas canvas) {
// block(canvas); If I call this here, draws behind children, still no updates
}
private void block(Canvas canvas) {
Blocker.block(canvas, this.getDrawingCache());
}
}
The problem I'm having is in my viewgroup. If I run the block method in the viewgroup's draw, it draws over everything but doesn't ever update when child views change. I've traced function calls with Log, and the draw method seems to be running, but nothing changes.
I've also tried implementing this in onDraw. This draws the bitmap behind all the children views, and again they aren't updating.
Can anyone explain how I would go about fixing this?
Try this:
#Override
protected void dispatchDraw(Canvas canvas) {
// call block() here if you want to draw behind children
super.dispatchDraw(canvas);
// call block() here if you want to draw over children
}
And call destroyDrawingCache() and then, buildDrawingCache() each time you change a child.
Draw() method will work well for you.
I'm now trying to make a count time view in a circle shape, when time is passing, the view will reducing his angle. It's used to cover profile photo(a circle shape photo).
Starting with Android API 23, you can use onDrawForeground(Canvas) to draw on top of child views: https://developer.android.com/reference/android/view/View#onDrawForeground(android.graphics.Canvas)
Unlike onDraw() though, be sure to call through to the super class:
#Override
public void onDrawForeground(final Canvas canvas) {
super.onDrawForeground(canvas);
// Your code here
}
I am developing with Google Maps Add-on. I found a little strange circle in the middle of my MapView. When I scroll the view or tap somewhere else, it's gone.
I debug the view with Hierarchy View tools, you can see is from inside of MapView, definitely not some misplace view from my code.
The red marker is from the overlay, just simply pin on the center of this MapView.
Here is the code, pretty standard MapView code:
mMapView = new MapView(getActivity(), R.string.key_mapAPIKey);
mMapView.setClickable(false);
mMapView.getController().setZoom(DEFAULT_MAP_ZOOM);
mMapView.getController().animateTo(geoPoint);
mMapView.getOverlays().clear();
mMapView.getOverlays().add(new GoogleMapClassicMarkerOverlay(getResources(), geoPoint));
UPDATE-10/30
The Overlay is also ordinary, just to show a single marker in the middle, I remove the overlay but the circle is still there:
public class GoogleMapClassicMarkerOverlay extends ItemizedOverlay<OverlayItem> {
private GeoPoint mGeoPoint;
private OverlayItem mItem;
public GoogleMapClassicMarkerOverlay(Resources res, GeoPoint geoPoint) {
super(boundCenterBottom(new SafeBitmapDrawable(res, R.drawable.map_marker)));
mGeoPoint = geoPoint;
mItem = new OverlayItem(mGeoPoint, "", "");
populate();
}
#Override
protected OverlayItem createItem(int i) {
return mItem;
}
#Override
public int size() {
return 1;
}
}
Anyone has idea how to remove this wired circle?
I'm porting a Google Maps based project to Osmdroid in order to use OpenStreetMaps. The port is working OK apart from adding my overlay which consists of a number of straight lines and some text. In both projects I add the Overlay by means of a timer thread and handler calling redrawOverlay.
In the OSM project my overlay is just a grey square completely hiding the map. If I remove the call to redrawOveraly, the OSM tiles are shown OK. I've reduced the overlay code to the bare minimum of a single diagonal line in the code samples below. It works fine in the Google app, overlaying the map tile. The com.google.android.maps.Overlay has a draw method, the OSM has an onDraw, so I have in the OSM version:
private MapView mv;
private MapOverlay mmapOverlay = null;
private void redrawOverlay() {
gPt = mv.getMapCenter();
if (mmapOverlay == null)
mmapOverlay = new MapOverlay(getApplicationContext());
List<Overlay> listOfOverlays = mv.getOverlays();
listOfOverlays.clear();
listOfOverlays.add(mmapOverlay);
mv.invalidate();
}
public class MapOverlay extends org.osmdroid.views.overlay.Overlay {
public MapOverlay(Context ctx) {
super(ctx);
// TODO Auto-generated constructor stub
}
#Override
public void onDraw(Canvas canvas, MapView mapView) {
Paint lp3;
lp3 = new Paint();
lp3.setColor(Color.RED);
lp3.setAntiAlias(true);
lp3.setStyle(Style.STROKE);
lp3.setStrokeWidth(1);
lp3.setTextAlign(Paint.Align.LEFT);
lp3.setTextSize(12);
canvas.drawLine(10, 10, 150, 150, lp3);
}
Whilst in the Google maps original I have the equivalent :
public class MapOverlay extends com.google.android.maps.Overlay {
#Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow,
long when) {
super.draw(canvas, mapView, shadow);
Paint lp3;
lp3 = new Paint();
.....etc.
redrawOverlay is the same except the instantiation of the overlay is just:
mmapOverlay = new MapOverlay();
All suggestions will be gratefully received
UPDATE Question for kurtzmarc:
Thanks for you help so far, I see that you are one of the authors of Osmdroid. I like what it's doing so far. I would like to suppress the 'jump to and zoom in' that you get on double tap. I'd like it to do nothing at all. I think it's probably hitting this bit in your source and doing the zoomInFixing:
private class MapViewDoubleClickListener implements GestureDetector.OnDoubleTapListener {
#Override
public boolean onDoubleTap(final MotionEvent e) {
for (int i = mOverlays.size() - 1; i >= 0; i--)
if (mOverlays.get(i).onDoubleTapUp(e, MapView.this))
return true;
final GeoPoint center = getProjection().fromPixels(e.getX(), e.getY());
return zoomInFixing(center);
}
It doesn't seem that I can override it. I'm using the 3.0.1 jar and the associated javadocs. I'm wondering if the Mapview's setTouchDelegate method would help, but there is no reference to it in the javadocs. Have you any suggestions please?
I'm not sure where you are calling redrawOverlay() from, but if you look at the MinimapOverlay you will see an example where something is drawn at a fixed location on the screen. In other words, you are drawing in screen coordinates not in map coordinates.
Example:
#Override
protected void onDraw(final Canvas pC, final MapView pOsmv) {
// Calculate the half-world size
final Rect viewportRect = new Rect();
final Projection projection = pOsmv.getProjection();
final int zoomLevel = projection.getZoomLevel();
final int tileZoom = projection.getTileMapZoom();
mWorldSize_2 = 1 << (zoomLevel + tileZoom - 1);
// Find what's on the screen
final BoundingBoxE6 boundingBox = projection.getBoundingBox();
final Point upperLeft = org.osmdroid.views.util.Mercator
.projectGeoPoint(boundingBox.getLatNorthE6(), boundingBox.getLonWestE6(),
zoomLevel + tileZoom, null);
final Point lowerRight = org.osmdroid.views.util.Mercator
.projectGeoPoint(boundingBox.getLatSouthE6(), boundingBox.getLonEastE6(), zoomLevel
+ tileZoom, null);
// Save the Mercator coordinates of what is on the screen
viewportRect.set(upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y);
// Offset into OSM coordinates
viewportRect.offset(-mWorldSize_2, -mWorldSize_2);
// Draw a line from one corner to the other
canvas.drawLine(viewportRect.left, viewportRect.top, viewportRect.right, viewportRect.bottom);
From here viewportRect represents the upper left to the lower right of the screen. You can use this to draw at any fixed points on the screen.
UPDATE:
To answer your second question - what you need to do is override onDoubleTap in your Overlay and return "true". Returning "true" indicates to the base class that you "consumed" the event and no further processing should take place. Take a look at the minimap overlay code for a good example:
http://code.google.com/p/osmdroid/source/browse/trunk/osmdroid-android/src/org/osmdroid/views/overlay/MinimapOverlay.java
We are right in the middle of overhauling the Overlays, so some of this will be handled a little better in the near future. For example, the getOverlays().clear() bug you ran into has also been reported elsewhere and we've since fixed it.