Reputation: 2229
When dragging a marker on a Google Maps map on Android, the marker is displayed below the finger which makes it almost impossible to precisely place the marker on the map. Is there a way to simply shift the marker by some pixels to the top to make it visible?
I tried moving the marker in onMarkerDragStart()
but it immediatly returns to its former position when I continue dragging. I also tried constantly shifting the markers position in onMarkerDrag()
. This gives unpredictable results when stopping to drag the marker.
Is there an official way to shift the marker while dragging it or has someone found a way to do so otherwise?
Upvotes: 2
Views: 3052
Reputation: 49
Here are the basic steps and below is the full code. Tested and works great.
First, assume we are using a marker icon bitmap, size 27 pixels by 27 pixels and you want to move the anchor point from the default lower left of the icon to the lower right.
Move the anchor point to the lower right: marker.setAnchor(1,0);
One icon width in the x direction is 27 DPs, but we need to know how many pixels that is.
offsetPoint.x -= MapUtils.convertDpToPx(CreateRouteActivity.this, offsetDPX);
Now we know where the point on the screen is, so just grab the LatLng from that: marker.setPosition(projection.fromScreenLocation(offsetPoint));
If you want to move the icon even further away from your finger, just experiment with the ANCHOR_FACTOR value.
Here is my CreateRouteActivity:
private GoogleMap.OnMarkerDragListener onMarkerDragListener = new GoogleMap.OnMarkerDragListener() {
float offsetDPX;
float offsetDPY;
Projection projection;
@Override
public void onMarkerDragStart(Marker marker) {
// Save the projection every time a marker starts to drag. This keeps the anchors and drop points synced up.
// If we don't save the projection at the point the marker starts to drag and the user zooms out, the icon pixel size
// will remain the same but the LatLng distances will have way fewer pixels, messing up the drop point.
projection = gMap().getProjection();
// GoogleMap's default anchor is located at the lower left point of the marker icon.
// An ANCHOR_FACTOR of 1 will move the drag point to the lower right of the icon.
// An ANCHOR_FACTOR of 2 will move the drag point to the lower right x 2. (The icon will move from under the user's finger to 2 width's above and to the left.)
float ANCHOR_FACTOR = 1f;
// 27 is my original icon's width in pixels.
// Android increases the pixel size of the image in high-density screens, so make sure you use 27 DPs, not 27 pixels.
offsetDPX = 27*ANCHOR_FACTOR;
offsetDPY = 27*(ANCHOR_FACTOR-1);
// Always set the anchor by percentage of icon width. 0,0 is lower left. 1,0 is lower right.
marker.setAnchor(ANCHOR_FACTOR,ANCHOR_FACTOR-1);
}
@Override
public void onMarkerDrag(Marker marker) {
// If you want something to happen while you drag, put it here.
}
@Override
public void onMarkerDragEnd(Marker marker) {
// getPosition returns pixel location
Point offsetPoint = projection.toScreenLocation(marker.getPosition());
// We need to offset by the number of DPs, so convert DPs to pixels.
offsetPoint.x -= MapUtils.convertDpToPx(CreateRouteActivity.this, offsetDPX);
offsetPoint.y -= MapUtils.convertDpToPx(CreateRouteActivity.this, offsetDPY);
// set the marker's LatLng from offsetPoint (in pixels)
marker.setPosition(projection.fromScreenLocation(offsetPoint));
};
And some conversion tools in my MapUtils object:
public static float convertDpToPx(Context context, float dp) {
return dp * context.getResources().getDisplayMetrics().density;
}
public static float convertPxToDp(Context context, float px) {
return px / context.getResources().getDisplayMetrics().density;
}
Upvotes: 0
Reputation: 44118
I got pretty interested in this myself, so I tried a bunch of ways to do it.
I finally figured out that by default Maps API moves the marker to the right by it's width, when it starts being dragged. To fake the [1, 1] anchor after the drag, we need to move the marker left horizontally (x-axis) by it's width, and up vertically (y-axis) by it's height.
This way seems to be working properly and is pretty neat.
Here's an example:
// Get marker dimensions
Bitmap img = BitmapFactory.decodeResource(getResources(), R.drawable.marker);
final int width = img.getWidth();
final int height = img.getHeight();
mMap.setOnMarkerDragListener(new GoogleMap.OnMarkerDragListener() {
@Override
public void onMarkerDragStart(final Marker marker) {
marker.setAnchor(1, 1);
}
@Override
public void onMarkerDrag(Marker marker) {
}
@Override
public void onMarkerDragEnd(final Marker marker) {
Projection projection = mMap.getProjection();
// Coordinates to point (in screen pixels)
Point point = projection.toScreenLocation(marker.getPosition());
// Move to [1, 1]
point.x -= width;
point.y -= height;
// Point to coordinates
LatLng coords = projection.fromScreenLocation(point);
// Reset the anchor and use the coordinates
marker.setAnchor(0, 0);
marker.setPosition(coords);
}
});
// Your marker
BitmapDescriptor bitmapDescriptor =
BitmapDescriptorFactory.fromResource(R.drawable.marker);
mMap.addMarker(new MarkerOptions()
.position(mPolylines.get(0).getPoints().get(1000))
.icon(bitmapDescriptor)
.anchor(0, 0)
.draggable(true));
Note: this answer suggests you may need to wait for the map to be fully inflated before you can use the map's projection. You may want to edit accordingly, though I found it works fine without it.
Upvotes: 1