Google Maps Android API - 如何在拖动时移动标记的锚点

Posted

技术标签:

【中文标题】Google Maps Android API - 如何在拖动时移动标记的锚点【英文标题】:Google Maps Android API - How to shift anchor of marker while dragging 【发布时间】:2014-10-02 04:37:22 【问题描述】:

android 上的 Google Maps 地图上拖动标记时,标记会显示在手指下方,因此几乎不可能在地图上精确放置标记。有没有办法简单地将标记移动一些像素到顶部以使其可见?

我尝试在onMarkerDragStart() 中移动标记,但当我继续拖动时,它立即返回到原来的位置。我还尝试不断改变onMarkerDrag() 中的标记位置。这会在停止拖动标记时产生不可预知的结果。

是否有官方方法可以在拖动标记时移动标记,或者是否有人找到了其他方法?

【问题讨论】:

【参考方案1】:

我自己对这个很感兴趣,所以我尝试了很多方法。

我终于发现默认情况下 Maps API 会在开始拖动标记时将标记向右移动它的宽度。要在拖动后伪造 [1, 1] 锚点,我们需要将标记向左水平(x 轴)移动宽度,垂直向上(y 轴)移动高度。

这种方式似乎工作正常,非常整洁。

这是一个例子:

// 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));

注意:this answer 建议您可能需要等待地图完全充气后才能使用地图的投影。您可能需要相应地进行编辑,尽管我发现没有它也可以正常工作。

【讨论】:

试过了,将锚点设置回 0,0 会使标记在地图上跳跃。我不想更改锚点(它指定标记图像中的哪个位置应该是地图上标记的实际位置),而是在拖动开始时向上移动标记,包括其锚点。 谢谢,我也试过了。你是对的,标记停留在该位置,但实际位置(纬度和经度)现在不符合预期。这是正常的,因为我们改变了锚点位置。 也许最简单的解决方案是使用一个标记符号,它的锚点不在底部中间,而是在顶部中间。这样在拖动时可能会看到它;-) @Hokascha 啊,你是对的,我的 美丽的解决方案 失败了:/ 但是你知道一旦开始拖动,API 会稍微移动标记,所以它们并不是真的那么隐形。 是的,看起来即使是谷歌也考虑到了这一点。来自 onMarkerDragStart() 文档:“可以通过 getPosition() 访问标记的位置;此位置可能与拖动开始之前的位置不同,因为标记在触摸点上方弹出。”因此,他们将其弹出了一点,但似乎无法控制标记弹出的距离。只需使用默认标记图像即可显示问题 - 除非您的手指是透明的,否则您看不到锚点。【参考方案2】:

这里是基本步骤,下面是完整代码。经过测试,效果很好。

首先,假设我们使用的是标记图标位图,大小为 27 x 27 像素,并且您希望将锚点从图标的默认左下角移动到右下角。 将锚点移至右下方:marker.setAnchor(1,0); 一个图标在 x 方向的宽度是 27 DPs,但我们需要知道这是多少像素。offsetPoint.x -= MapUtils.convertDpToPx(CreateRouteActivity.this, offsetDPX); 现在我们知道屏幕上的点在哪里,所以只需从中获取 LatLng:marker.setPosition(projection.fromScreenLocation(offsetPoint)); 如果您想将图标移到离手指更远的位置,只需尝试使用 ANCHOR_FACTOR 值即可。

这是我的 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));
    ;

以及我的 MapUtils 对象中的一些转换工具:

    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;
    

【讨论】:

以上是关于Google Maps Android API - 如何在拖动时移动标记的锚点的主要内容,如果未能解决你的问题,请参考以下文章

Google Geolocation API 和 Google Maps Android SDK

Android:使用 Google API 获取实时方向,还是与 Google Maps 通信?

Android Manifest 未向 Google Maps 提供 API 密钥

为 Google Maps API v2 Android 发布流媒体方向

Android、Google Maps Android api、设置位置和地图类型

带有 API KEY 的 Google Maps V2 Android 显示为空白