4

その中に MapView と ListView を含むアクティビティを作成しようとしています。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();
            }
        }

MapView にマーカーを追加する Activity メソッド

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 が全画面表示にならないことが原因ではないかと考えました。MapView が画面全体を占有できるように ListView を削除すると、画面上で触れた正確な位置にマーカーを配置できました。

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

2 に答える 2

0

やってみました:

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

これにより、Drawable描画時に地理座標を (x,y)(0,0) 座標にするのではなく、マップ上の中心点に置くことができます。

編集::base()コンストラク ターの BoundCenterBottom(pin) を使用すると、ここで問題を再現することはできませんDrawable

また、あなたの方法で気づいたのですが、毎回AddPinToMap新しいものを作成しています。PinOverlayこれは必要ではなく、マップに 1 回だけ追加する に新しい を追加するだけですOverlayItemPinOverlayマップに多くのピンを追加する場合、これはより効率的で、リソースの消費が少なくなります。

したがって、次のようになります。

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

また、削除できるようにしたい場合は、Removeメソッドを追加します。PinOverlayOverlayItems

また、クラスをオーバーライドする代わりにフックできるイベントも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
}
于 2012-12-13T22:23:00.080 に答える
0

最後に、問題の原因を見つけました。

プロジェクションは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;
}
于 2012-12-20T13:10:36.723 に答える