安卓地图视图中叠加层位置不准确

4

我正在尝试创建一个包含MapView和ListView的Activity。我使用C#和MonoDroid。

布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <app.MD.UI.PlacesMapView
        android:id="@+id/mapView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:enabled="true"
        android:clickable="true"
        android:layout_weight="1"
        android:apiKey="key" />
    <ListView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:id="@+id/resultsTable" />
</LinearLayout>

我的自定义MapView类实现了长按处理程序

private void HandleLongPress(MotionEvent e)
        {
            if (e.Action == MotionEventActions.Down) {
                // Finger has touched screen.
                longpressTimer = new Timer(LONGPRESS_THRESHOLD);
                longpressTimer.AutoReset = false;
                longpressTimer.Elapsed += delegate(object sender, ElapsedEventArgs e_elapsed) {
                    GeoPoint longpressLocation = Projection.FromPixels((int)e.GetX(), (int)e.GetY());
                    if (OnLongPress != null) {
                        OnMapViewLongPressEventArgs args = new OnMapViewLongPressEventArgs();
                        args.location = longpressLocation;
                        OnLongPress(this, args);
                    }
                };

                longpressTimer.Start();

                lastMapCenter = MapCenter;
            }

            if (e.Action == MotionEventActions.Move) {
                if (!MapCenter.Equals(lastMapCenter)) {
                    longpressTimer.Stop();
                }

                lastMapCenter = MapCenter;
            }

            if (e.Action == MotionEventActions.Up) {
                // User has removed finger from map.
                longpressTimer.Stop();
            }

            if (e.PointerCount > 1) {
                // This is a multitouch event, probably zooming.
                longpressTimer.Stop();
            }
        }

Activity方法,用于将标记添加到MapView

protected void AddPinToMap(GeoPoint location, string title, bool scrollTo, int zoom)
        {
            var pin = Resources.GetDrawable(Resource.Drawable.maps_pin);
            PinOverlay pinOverlay = new PinOverlay(pin, this);
            OverlayItem item = new OverlayItem(location, title, "");
            pinOverlay.AddOverlay(item);
            mapView.Overlays.Clear();
            mapView.Overlays.Add(pinOverlay);

            if (scrollTo) {
                mapView.Controller.AnimateTo(location);
            }

            mapView.Controller.SetZoom(zoom);
        }

这是我的PinOverlay类。
class PinOverlay : ItemizedOverlay
    {
        private List<OverlayItem> _items = new List<OverlayItem>();
        private Context _context;

        public PinOverlay(Drawable pin, Context context) : base(pin)
        {
            _context = context;
            BoundCenterBottom(pin);
            Populate();
        }

        protected override Java.Lang.Object CreateItem(int i)
        {
            return _items[i];
        }

        public override int Size()
        {
            return _items.Count();
        }

        public void AddOverlay(OverlayItem overlay) {
            _items.Add(overlay);
            Populate();
        }
    }

我的问题是,当我触摸MapView时,我的标记显示在我所触摸的点以下。 我认为这可能是由于我的MapView没有占满整个屏幕所导致的。 当我删除了ListView以便MapView可以占据整个屏幕时,我能够将标记放在我所触摸的确切点上。

如果MapView只占据部分屏幕,有什么建议或示例如何处理触摸事件?

更新 找到一种解决方法,需要使用不同大小的屏幕进行测试。

转换触摸坐标

public static Point ConvertCoordinates(Context context, MapView mapView, double x, double y)
        {
            var wm = context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
            var display = wm.DefaultDisplay;

            double diffY = (Convert.ToDouble(display.Height) / 2 - Convert.ToDouble(mapView.Height) / 2) / 2 - 20;
            double diffX = Convert.ToDouble(display.Width) / 2 - Convert.ToDouble(mapView.Width) / 2;

            Point size = new Point();
            size.X = (int)(x - diffX);
            size.Y = (int)(y - diffY);

            return size;
        }

使用方法如下:

var point = MapHelper.ConvertCoordinates(this.Context, this, e.GetX(), e.GetY());

GeoPoint longpressLocation = Projection.FromPixels(point.X, point.Y);
2个回答

0

你尝试过了吗:

public PinOverlay(Drawable pin, Context context) 
    : base(BoundCenterBottom(pin))

这将使您放在地图上的Drawable居中于该点,而不是在绘制时将地理坐标作为(x,y)(0,0)坐标。

编辑: 我似乎无法在此处重现您的问题,在构造函数的:base()中使用BoundCenterBottom(pin)可以很好地将我的Drawable居中。

另外,我注意到在您的AddPinToMap方法中,每次都会创建一个新的PinOverlay。这是不必要的,您应该只向PinOverlay添加新的OverlayItem,然后将其添加到地图上仅1次。如果您要在地图上添加很多标记,则这更有效率且资源消耗更少。

因此,它应该看起来更像:

protected void AddPinToMap(GeoPoint location, string title, bool scrollTo, int zoom)
{
    var pin = Resources.GetDrawable(Resource.Drawable.maps_pin);
    OverlayItem item = new OverlayItem(location, title, "");
    pinOverlay.AddOverlay(item); // this is instantiated in OnCreate

    if (scrollTo) {
        mapView.Controller.AnimateTo(location);
    }

    mapView.Controller.SetZoom(zoom);
}

如果您想要删除OverlayItems,请向PinOverlay添加一个Remove方法。

此外,MapView已经有一个LongClick事件,您可以将其连接到而不是覆盖该类:

var mapView = FindViewById<MapView>(Resource.Id.myMapView);
mapView.LongClick += (sender, args) => { //do whatever in here };

或者

var mapView = FindViewById<MapView>(Resource.Id.myMapView);
mapView.LongClick += OnLongClick;

private void OnLongClick(object sender, LongClickEventArgs longClickEventArgs)
{
    // do whatever in here
}

是的,我已经尝试过这种方法,但并没有什么区别。即使我明确设置了Bounds,它也只会影响标记的阴影位置。 - Mikhail Kuznetsov
感谢您对PinOverlay的建议。 - Mikhail Kuznetsov
另外,我找不到如何从LongClickEventArgs中获取XY,所以我正在使用计时器和触摸事件。 - Mikhail Kuznetsov

0

终于找到了问题的原因。

这部分是错误的,因为ProjectionElapsed委托内被访问。但是在那个时候,Projection已经改变了它的状态,导致坐标错误。

if (e.Action == MotionEventActions.Down) {
    // Finger has touched screen.
    longpressTimer = new Timer(LONGPRESS_THRESHOLD);
    longpressTimer.AutoReset = false;
    longpressTimer.Elapsed += delegate(object sender, ElapsedEventArgs e_elapsed) {
        GeoPoint longpressLocation = Projection.FromPixels((int)e.GetX(), (int)e.GetY());
        if (OnLongPress != null) {
             OnMapViewLongPressEventArgs args = new OnMapViewLongPressEventArgs();
             args.location = longpressLocation;
             OnLongPress(this, args);
        }
    };

    longpressTimer.Start();

    lastMapCenter = MapCenter;
}

所以解决方案是在 OnTouchEvent 的最开始获取 GeoPoint
if (e.Action == MotionEventActions.Down) {
    capturedProjection = Projection.FromPixels((int)e.GetX(), (int)e.GetY());

    // Finger has touched screen.
    longpressTimer = new Timer(LONGPRESS_THRESHOLD);
    longpressTimer.AutoReset = false;
    longpressTimer.Elapsed += (object sender, ElapsedEventArgs e_timer) => {
        if (OnLongPress != null) {
            OnMapViewLongPressEventArgs args = new OnMapViewLongPressEventArgs();
            args.location = capturedProjection;
            OnLongPress(this, args);
        }
    };

    longpressTimer.Start();

    lastMapCenter = MapCenter;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接