【问题标题】:Inaccurate positioning of overlays in Android's MapViewAndroid 的 MapView 中的叠加层定位不准确
【发布时间】:2012-12-13 12:39:31
【问题描述】:

我正在尝试创建一个包含 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 只占据屏幕的一部分,关于如何处理触摸事件的任何建议或示例?

UPD找到了某种解决方法,仍然需要用不同的屏幕尺寸进行测试

转换触摸坐标

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

【问题讨论】:

    标签: android android-mapview xamarin.android itemizedoverlay


    【解决方案1】:

    你试过了吗:

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

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

    编辑: 我似乎无法在这里重现您的问题,当在构造函数的:base() 中有BoundCenterBottom(pin) 时,它将我的Drawable 居中就好了。

    我还注意到在您的AddPinToMap 方法中,您每次都会创建一个新的PinOverlay。这应该不是必需的,您只需将新的 OverlayItems 添加到您仅在地图中添加 1 次的 PinOverlay。如果您要在地图上添加大量图钉,这会更高效且占用更少的资源。

    所以它应该看起来更像:

    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,请将 Remove 方法添加到 PinOverlay

    另外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,它也只会影响标记阴影的位置。
    • 感谢 PinOverlay 的建议。
    • 另外,我找不到如何从 LongClickEventArgs 获取 XY,这就是我使用计时器和触摸事件的原因。
    【解决方案2】:

    终于找到问题的原因了。

    这部分是错误的,因为 Projection 是在 Elapsed 委托中访问的。但是此时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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-06
      • 1970-01-01
      相关资源
      最近更新 更多