Android MapView marker shadows move after activity resumes - android

I have a MapView, with various markers on it. On entering the MapActivity the first time, I set the bounds of drawables set on the markers, and everything appears fine. The markers and shadows all appear correctly. But when I click over to another activity, and return, the shadows, or the markers are no longer bound correctly. Sometimes its only some of the markers that are affected. Can anyone tell me what is going on here? Here is some code. Below is where I set the bounds on the marker drawable:
InputStream is = assetManager.open(imageName);
drawable = Drawable.createFromStream(is, null);
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
drawable.setBounds(-width / 2, -height, width - (width / 2), 0);
And here is how I create/add the overlay item to the map:
GeoPoint point = createGeoPoint(lat, lon);
OverlayItem overlayItem = new OverlayItem(point, character.get_id(), "");
Drawable image = drawable;
overlayItem.setMarker(image);
itemizedOverlay.addOverlay(overlayItem);
From what I can tell, the overlays are not being re-added to the map, this is simply a redraw after an onResume() event. Any ideas?
Update: I've actually figured out, only images of the type tapped move, and its not the shadow, but the image that moves. So, of there are x images of type A, and y images of type B, and I tap on any image of type A, all images of type A are misaligned with their shadows when I return to the MapActivity, while all images of type B are still properly aligned.
Another update: I am outputting the bounds to the log, and see that the bounds are changing after returning to the map screen. When the marker is initially added, its bounds are:
[Rect(-25, -48 - 25, 1)]
But when returning to the map screen, the bounds have changed to:
[Rect(0, 0 - 50, 49)]
...why would this be?
Yet another update: I have a workaround... but I would still like an answer as to why this is happening. The workaround is to override the draw method, and set the bounds (to center bottom) whenever the draw method is called. Here is the code:
#Override
public void draw(Canvas canvas) {
int dWidth = getIntrinsicWidth();
int dHeight = getIntrinsicHeight();
setBounds(-dWidth / 2, -dHeight, dWidth / 2, 0);
super.draw(canvas);
}

I found out what the problem was. You can see from the code in the question, I am reading in BitmapDrawables from an InputStream. To reduce waste, I put all images created in a flyweight and reuse them when I can. That means, when I put 3 'sunshines' on the map, all three are based on the same image. In addition, when I place them on the map via an ItemizedOverlay, I am calling boundCenterBottom() so the shadow will display in the correct place.
When I open a different Activity, that Activity has an ImageView that I was populating with the same image. Turns out, that ImageView must have been resetting the bounds. I know this because, after overriding the draw() method, and setting the bounds on every draw, I was no longer able to see the images in the ImageViews. When I comment out the overridden draw() method, I am able to see the images in the ImageViews, but the images are shifted when I return to the map. This tells me that adding the image to the ImageView must be updating the bounds.
My solution is to create two distinct images for each image type; one for the map, and one for the ImageViews on the other Activities. I can still put them in the flyweight, but they have separate keys so multiple image instances can exist for the same image.
Hope this helps someone. I know there was another question of StackOverflow about this, but I can't find it to link to or to answer.

Related

Simultaneously move six points in circular paths around their own orbital centers

Suppose I have six points drawn in a Canvas along the circumference of a circle, in the pattern of a hexagon.
I want to simultaneously move and re-draw each of these six points in a small circular path - for example, each point moves along the circumference of a circle with 1/10th the radius of the larger circle.
I'm a little lost on how to do this with Canvas and onDraw and I don't know if better solutions exist. How would you do this?
Some answers have at least pointed me toward this which shows how a point might move along a circular path, but I don't know how to implement it for this situation:
for (double t = 0; t < 2*Pi; t += 0.01)
{
x = R*cos(t) + x_0;
y = R*sin(t) + y_0;
}
Thank you!
Here's one approach:
Start with a custom view that extends View and overrides onDraw. You are on the right track with using the drawing functions of Canvas.
Give your custom view a field to hold the current angle:
private float mTheta;
Then add a method like:
public void setTheta(float radians) {
mTheta = radians;
invalidate();
}
Then in onDraw, use the current value of mTheta to calculate the position of your points.
Of course, with the custom view you might need to handle sizing with an onMeasure override and possibly some layout with an onLayout override. If you set the dimensions of the view to an absolute dp value, the default behavior should work for you.
Doing it this way will set you up to override onTouch and allow user interaction to move the graphics, or use a ValueAnimator to cycle from 0 to 2π calling setTheta from within your AnimatorUpdateListener.
Put some code together and when you get stuck, post another question. If you add a comment to this answer with a link to the new question, I'll take a look at it.

Issue with layouts position/dimensions before they are getting displayed

I have a piece of code where I am displaying some pins (Button with an image) on an image (full screen ImageView). (You can assume that all the pins are created dynamically and have a random position). The requirement is that those pins should not overlap. For each pin created I am checking if it overlaps with any of the other previously created pins.
To do that I am trying to get the Rect of those pins and check if they overlap using Rect.intersects().
The issue is that I cannot use functions like getRight(), getTop(), getWidth(), getHeight() before the view is actually displayed. They all return 0. So I cannot get the correct data for the rectangles.
I tried to put the code in different callbacks like onWindowFocusChanged() or onResume() but I still got the same issue, all dimensions/positions return 0.
I am looking for a solution/advice regarding this problem. How to get the correct positions and dimensions before the pins are displayed ?
I looked at View.measure(), it seems I can get some values using getMeasuredWidth() and getMeasuredHeight() but getRight() and getTop() still return 0.
Add invisible rect first and check the position then move this position as per requirements and the make this visible. This will do the trick.

android custom map marker bounds

Hello i am using a resized version of this marker( http://www.clker.com/clipart-orange-pin-4.html ) for showing markers in google maps on android.
The problem is i don't know how to make the marker point match the coordinates.
The arrow point is at about 1/5 of the Width coordinates and MAX of the Height.
here is my class
public class GestionaleItemizedOverlay extends com.google.android.maps.ItemizedOverlay {
public GestionaleItemizedOverlay(Drawable defaultMarker, Context context) {
//super(boundCenterBottom(defaultMarker));
super(boundCenter(defaultMarker));
this.mContext = context;
}
...
And this
this.marker_poi = this.getContext().getResources().getDrawable(R.drawable.marker);
this.marker_poi.setBounds(this.marker_poi.getIntrinsicWidth() / 2, this.marker_poi.getIntrinsicHeight(), this.marker_poi.getIntrinsicWidth() / 2, 0);
new GestionaleItemizedOverlay(this.poi, this.context);
Do i need to setBounds on the marker before passing it to the constructor? and why does super(defaultMarker) makes all the markers not to show ?
The previous answer is right, you need to play with the BOUNDS value of your marker.
As I am sure you know the bounding rectangle determines the size of the object... The marker on your map is going to lay itself out with upper left coordinate of your marker at the GPS coordinate if you just would call a normal setbounds on the size of your image.
What you need to do is modify the bounds on your image so that the part of the image you wnat is over the point on the map you want.
So if your resized image is say 20 x 20, and your actual arrow tip (assuming the image is an arrow, and you want it lined up exactly with the point on the map) is at say X=4 and Y=10, you need to modify the bounds of your image accordingly to "move" the image where you want it.
Since 0,0 would put the image 4 pixels to the right, and 10 pixels below the actual point on the map you associate it with, your bounds should not be 0,0,X,Y but should be -4,-10,X size of image-4,Y size of image-10 (in this exampe case the xsize of image and ysize of image are both 20, but should use the values appropriate for your case.)
Yes you need to setBounds but you can do it in the constructor, this is what I do in the constructor to set it to the left bottom corner:
public class CustomOverlay extends ItemizedOverlay<CustomOverlayItem> {
public CustomOverlay(Drawable defaultMarker, Context context) {
super(defaultMarker);
defaultMarker.setBounds(0, -defaultMarker.getIntrinsicHeight(), defaultMarker.getIntrinsicWidth(), 0);
}
You'll need to add a little extra to the left side of the bounds to make center point at the right position. You should remember this is set in px so make some kind of DP converter so it is set properly for your different images for each screen size.
About the second question, you'll need a context in order to know where to add the overlay. Without the context you just create your overlay without giving it any reference to where it should be used (you could add this later if you wanted to).

boundCenter() / boundCenterBottom() obligatory for markers in ItemizedOverlay?

It seems that markers in an ItemizedOverlay are not shown in the map unless you call boundCenter() or boundCenterBottom() on the default marker.
Is this correct, and if so, why? Is it that a (0,0) point is not set at all by default, and thus Android has no idea how to place the markers?
Update:
I see that you also have Drawable.setBounds(), so I assume boundCenter() and boundCenterBottom() is calling this method. How is it decided which part of the Drawable is used as the pin point on the map?
See this example of how to use the boundCenter()
I believe for the boundCenter() call to work, your Drawable should have some intrinsic width and height. I'm guessing it works something like this:
public static Drawable boundCenter(Drawable d)
{
d.setBounds(d.getIntrinsicWidth() /- 2, d.getIntrinsicHeight() / -2,
d.getIntrinsicWidth / 2, d.getIntrinsicHeight() / 2);
return d;
}
This offsets the bounds from Top/Left, to Bottom/Center.
You must set the BOUNDS in some fashion if you wish them to appear. It doesn't matter which method you use, but if there are no bounds on your drawable, its bounds are 0,0,0,0 or something like that, and you will see nothing.

Dynamically create / draw images to put in android view

I'm not sure I'm doing this the "right" way, so I'm open to other options as well. Here's what I'm trying to accomplish:
I want a view which contains a graph. The graph should be dynamically created by the app itself. The graph should be zoom-able, and will probably start out larger than the screen (800x600 or so)
I'm planning on starting out simple, just a scatter plot. Eventually, I want a scatter plot with a fit line and error bars with axis that stay on the screen while the graph is zoomed ... so that probably means three images overlaid with zoom functions tied together.
I've already built a view that can take a drawable, can use focused pinch-zoom and drag, can auto-scale images, can switch images dynamically, and takes images larger than the screen. Tying the images together shouldn't be an issue.
I can't, however, figure out how to dynamically draw simple images.
For instance: Do I get a BitMap object and draw on it pixel by pixel? I wanted to work with some of the ShapeDrawables, but it seems they can only draw a shape onto a canvas ... how then do I get a bitmap of all those shapes into my view? Or alternately, do I have to dynamically redraw /all/ of the image I want to portray in the "onDraw" routine of my view every time it moves or zooms?
I think the "perfect" solution would be to use the ShapeDrawable (or something like it to draw lines and label them) to draw the axis with the onDraw method of the view ... keep them current and at the right level ... then overlay a pre-produced image of the data points / fit curve / etc that can be zoomed and moved. That should be possible with white set to an alpha on the graph image.
PS: The graph image shouldn't actually /change/ while on the view. It's just zooming and being dragged. The axis will probably actually change with movement. So pre-producing the graph before (or immediately upon) entering the view would be optimal. But I've also noticed that scaling works really well with vector images ... which also sounds appropriate (rather than a bitmap?).
So I'm looking for some general guidance. Tried reading up on the BitMap, ShapeDrawable, Drawable, etc classes and just can't seem to find the right fit. That makes me think I'm barking up the wrong tree and someone with some more experience can point me in the right direction. Hopefully I didn't waste my time building the zoom-able view I put together yesterday :).
First off, it is never a waste of time writing code if you learned something from it. :-)
There is unfortunately still no support for drawing vector images in Android. So bitmap is what you get.
I think the bit you are missing is that you can create a Canvas any time you want to draw on a bitmap. You don't have to wait for onDraw to give you one.
So at some point (from onCreate, when data changes etc), create your own Bitmap of whatever size you want.
Here is some psuedo code (not tested)
Bitmap mGraph;
void init() {
// look at Bitmap.Config to determine config type
mGraph = new Bitmap(width, height, config);
Canvas c = new Canvas(mybits);
// use Canvas draw routines to draw your graph
}
// Then in onDraw you can draw to the on screen Canvas from your bitmap.
protected void onDraw(Canvas canvas) {
Rect dstRect = new Rect(0,0,viewWidth, viewHeight);
Rect sourceRect = new Rect();
// do something creative here to pick the source rect from your graph bitmap
// based on zoom and pan
sourceRect.set(10,10,100,100);
// draw to the screen
canvas.drawBitmap(mGraph, sourceRect, dstRect, graphPaint);
}
Hope that helps a bit.

Categories

Resources