23

マップビューを長押しして、マップ上のそのポイントに場所マーカーを表示するにはどうすればよいですか?

私は成功せずにいくつかの方法を試しました:

1)ロングクリックを検出しなかっsetOnLongClickListenerた で使用する。MapvView

2) 私の他のアイデアは、拡張MapViewしてオーバーライドすることでしdispatchTouchEventた.. 長押しコールバックに応答する GestureDetector を作成します。しかし、サブクラス化された Mapview へのハンドルを取得できなかったため、ここで途中で立ち往生しました。すなわち

MyMapview mymapview; //MyMapView extends MapView

//results in a classcast exception
mymapView = (MyMapView) findViewById(R.id.map);

3)これを試す方法を私が知っている他の唯一の方法はMotionEvent.ACTION_DOWN、遅延ランナブルを検出してハンドラーに投稿し、他の2つのイベント(acton_moveまたはaction_up)が発生していない場合にロングプレスを検出することです。

長押しを検出するこれらの方法のいずれかについて誰かが考えを提供できますか?

4

8 に答える 8

22

さらに簡単な方法を見つけました。何も描画しないリストの最初のオーバーレイとしてオーバーレイを作成し、GestureDetector を使用してジェスチャを認識するために使用します。イベントを処理した場合は true を返して、伝播されないようにする必要があります。

    List<Overlay> overlays = mapView.getOverlays();
    overlays.clear();
    overlays.add(new MapGestureDetectorOverlay(new MyOnGestureListener()));

そして、ここにクラスがあります:

public class MapGestureDetectorOverlay extends Overlay implements OnGestureListener {
 private GestureDetector gestureDetector;
 private OnGestureListener onGestureListener;

 public MapGestureDetectorOverlay() {
  gestureDetector = new GestureDetector(this);
 }

 public MapGestureDetectorOverlay(OnGestureListener onGestureListener) {
  this();
  setOnGestureListener(onGestureListener);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event, MapView mapView) {
  if (gestureDetector.onTouchEvent(event)) {
   return true;
  }
  return false;
 }

 @Override
 public boolean onDown(MotionEvent e) {
  if (onGestureListener != null) {
   return onGestureListener.onDown(e);
  }
  return false;
 }

 @Override
 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
   float velocityY) {
  if (onGestureListener != null) {
   return onGestureListener.onFling(e1, e2, velocityX, velocityY);
  }
  return false;
 }

 @Override
 public void onLongPress(MotionEvent e) {
  if (onGestureListener != null) {
   onGestureListener.onLongPress(e);
  }
 }

 @Override
 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
   float distanceY) {
  if (onGestureListener != null) {
   onGestureListener.onScroll(e1, e2, distanceX, distanceY);
  }
  return false;
 }

 @Override
 public void onShowPress(MotionEvent e) {
  if (onGestureListener != null) {
   onGestureListener.onShowPress(e);
  }
 }

 @Override
 public boolean onSingleTapUp(MotionEvent e) {
  if (onGestureListener != null) {
   onGestureListener.onSingleTapUp(e);
  }
  return false;
 }

 public boolean isLongpressEnabled() {
  return gestureDetector.isLongpressEnabled();
 }

 public void setIsLongpressEnabled(boolean isLongpressEnabled) {
  gestureDetector.setIsLongpressEnabled(isLongpressEnabled);
 }

 public OnGestureListener getOnGestureListener() {
  return onGestureListener;
 }

 public void setOnGestureListener(OnGestureListener onGestureListener) {
  this.onGestureListener = onGestureListener;
 }
}
于 2010-06-07T15:23:26.623 に答える
8

私が知っている最善の方法は、オープン ソースのmapview-overlay-managerを使用し、コールバックを提供するジェスチャ リスナーを使用することです。

public void onLongPress(MotionEvent e, ManagedOverlay overlay)
于 2009-11-05T15:00:52.953 に答える
4

これは MapActivity で機能し、次のことが可能になります。

  1. マップ上での長押しを構成する時間のしきい値を独自に設定します (0.8 秒が適切です)。
  2. スクロール、マルチタッチ、または非マルチタッチ イベントを長押しとして解釈しません。また、誰かが長押ししているときに指が画面上をわずかに動く許容範囲を設定することもできます。

このソリューションは、 Roger Kristiansen のソリューションに基づいています。スクロールが長押しに見えないように、x と y の点検出を追加しました。このソリューションは、Roger のように MapView を拡張してリスナーを作成するのではなく、クラス変数を使用して既存の MapActivity にコードを配置するため、Roger ほどエレガントではありません。しかし、彼のコードに固執したいが、マルチタッチ以外のサポートを改善したい場合は、私のものから x と y ポイントのものを取り出して、彼のものに追加してください。


私の MapActivity の上部に設定されたクラス変数:

//variable for determining long press and then automatically adding a pin to the map
private int minMillisecondThresholdForLongClick = 800;
private long startTimeForLongClick = 0;
private float xScreenCoordinateForLongClick;
private float yScreenCoordinateForLongClick;
private float xtolerance=10;//x pixels that your finger can be off but still constitute a long press
private float ytolerance=10;//y pixels that your finger can be off but still constitute a long press
private float xlow; //actual screen coordinate when you subtract the tolerance
private float xhigh; //actual screen coordinate when you add the tolerance
private float ylow; //actual screen coordinate when you subtract the tolerance
private float yhigh; //actual screen coordinate when you add the tolerance

この関数を MapActivity に追加します。

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        /* We want to capture the place the user long pressed on the map and add a marker (pin) on the map at that lat/long.
         * This solution:
         *  1. Allows you to set the time threshold for what constitutes a long press
         *  2. Doesn't get fooled by scrolling, multi-touch, or non-multi-touch events

         * Thank you Roger Kind Kristiansen for the main idea
         */     

        //get the action from the MotionEvent: down, move, or up
        int actionType = ev.getAction();

        if (actionType == MotionEvent.ACTION_DOWN) {
            //user pressed the button down so let's initialize the main variables that we care about:
            // later on when the "Action Up" event fires, the "DownTime" should match the "startTimeForLongClick" that we set here  
            // the coordinate on the screen should not change much during the long press
            startTimeForLongClick=ev.getEventTime();
            xScreenCoordinateForLongClick=ev.getX();
            yScreenCoordinateForLongClick=ev.getY();

        } else if (actionType == MotionEvent.ACTION_MOVE) {
            //For non-long press actions, the move action can happen a lot between ACTION_DOWN and ACTION_UP                    
            if (ev.getPointerCount()>1) {
                //easiest way to detect a multi-touch even is if the pointer count is greater than 1
                //next thing to look at is if the x and y coordinates of the person's finger change.
                startTimeForLongClick=0; //instead of a timer, just reset this class variable and in our ACTION_UP event, the DownTime value will not match and so we can reset.                        
            } else {
                //I know that I am getting to the same action as above, startTimeForLongClick=0, but I want the processor
                //to quickly skip over this step if it detects the pointer count > 1 above
                float xmove = ev.getX(); //where is their finger now?                   
                float ymove = ev.getY();
                //these next four values allow you set a tiny box around their finger in case
                //they don't perfectly keep their finger still on a long click.
                xlow = xScreenCoordinateForLongClick - xtolerance;
                xhigh= xScreenCoordinateForLongClick + xtolerance;
                ylow = yScreenCoordinateForLongClick - ytolerance;
                yhigh= yScreenCoordinateForLongClick + ytolerance;
                if ((xmove<xlow || xmove> xhigh) || (ymove<ylow || ymove> yhigh)){
                    //out of the range of an acceptable long press, reset the whole process
                    startTimeForLongClick=0;
                }
            }

        } else if (actionType == MotionEvent.ACTION_UP) {
            //determine if this was a long click:
            long eventTime = ev.getEventTime();
            long downTime = ev.getDownTime(); //this value will match the startTimeForLongClick variable as long as we didn't reset the startTimeForLongClick variable because we detected nonsense that invalidated a long press in the ACTION_MOVE block

            //make sure the start time for the original "down event" is the same as this event's "downTime"
            if (startTimeForLongClick==downTime){ 
                //see if the event time minus the start time is within the threshold
                if ((eventTime-startTimeForLongClick)>minMillisecondThresholdForLongClick){ 
                    //make sure we are at the same spot where we started the long click
                    float xup = ev.getX();                  
                    float yup = ev.getY();
                    //I don't want the overhead of a function call:
                    xlow = xScreenCoordinateForLongClick - xtolerance;
                    xhigh= xScreenCoordinateForLongClick + xtolerance;
                    ylow = yScreenCoordinateForLongClick - ytolerance;
                    yhigh= yScreenCoordinateForLongClick + ytolerance;
                    if ((xup>xlow && xup<xhigh) && (yup>ylow && yup<yhigh)){ 

                        //**** safe to process your code for an actual long press **** 
                        //comment out these next rows after you confirm in logcat that the long press works
                        long totaltime=eventTime-startTimeForLongClick;
                        String strtotaltime=Long.toString(totaltime);                               
                        Log.d("long press detected: ", strtotaltime);


                        /* 
                        //Now get the latitude/longitude of where you clicked.  Replace all the code below if you already know how to translate a screen coordinate to lat/long.  I know it works though.

                        //*****************
                        //I have my map under a tab so I have to account for the tab height and the notification bar at the top of the phone.  
                        // Maybe there are other ways so just ignore this if you already know how to get the lat/long of the pixels that were pressed.
                        int TabHeightAdjustmentPixels=tabHost.getTabWidget().getChildAt(0).getLayoutParams().height;
                        int EntireTabViewHeight = tabHost.getHeight();
                        Display display = getWindowManager().getDefaultDisplay(); 
                        int EntireScreenHeight = display.getHeight();
                        int NotificationBarHeight=EntireScreenHeight-EntireTabViewHeight;
                        //the projection is mapping pixels to where you touch on the screen.
                        Projection proj = mapView.getProjection();
                        GeoPoint loc = proj.fromPixels((int)(ev.getX(ev.getPointerCount()-1)), (int)(ev.getY(ev.getPointerCount()-1)-TabHeightAdjustmentPixels-NotificationBarHeight)); 
                        int longitude=loc.getLongitudeE6();             
                        int latitude=loc.getLatitudeE6();
                        //*****************

                        //**** here's where you add code to: 
                        // put a marker on the map, save the point to your SQLite database, etc
                        */

                    }
                }
            }

        }


        return super.dispatchTouchEvent(ev);
    }
于 2012-02-21T05:56:11.517 に答える
4

マップでの長押しを処理するための実装がここにあります:

http://www.kind-kristiansen.no/2011/android-handling-longpresslongclick-on-map-revisited/

于 2011-10-29T11:32:15.243 に答える
3

この mapview-overlay-manager ライブラリは素晴らしいです。「スクロール」時に onLongPress がトリガーされる理由は、ライブラリがマルチタッチを考慮していないためです。複数のポインターが関与している場合は、 onLongPress を拒否することでこれを回避できます。

public void onLongPress(MotionEvent motionEvent, ManagedOverlay arg1) {

    if (motionEvent.getPointerCount() > 1) return;

    ... your logic here ...
}
于 2011-05-05T00:17:41.890 に答える
2

アンブローズ、

ライブラリmapview-overlay-managerのデモを修正しました。このコードをダブルタップ ジェスチャで実行するには:

package de.android1.overlaymanager.demo;

import android.os.Bundle;
import android.widget.Toast;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import com.google.android.maps.MapActivity;

import com.google.android.maps.MapView;
import com.google.android.maps.MapController;
import com.google.android.maps.GeoPoint;

import de.android1.overlaymanager.*;


public class DemoView extends MapActivity {

    MapView mapView;
    MapController mapController;

    OverlayManager overlayManager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mapView = (MapView) findViewById(R.id.mapview);
        mapView.setBuiltInZoomControls(true);
        mapController = mapView.getController();

        overlayManager = new OverlayManager(getApplication(), mapView);
    }

    @Override
    public void onWindowFocusChanged(boolean b) {

         createOverlayWithListener();

    }


    public void createOverlayWithListener() {
        //This time we use our own marker
        final ManagedOverlay managedOverlay = overlayManager.createOverlay("listenerOverlay", getResources().getDrawable(R.drawable.marker));
        for (int i = 0; i < 40; i = i + 3) {
            managedOverlay.createItem(GeoHelper.geopoint[i], "Item" + i);
        }
        managedOverlay.setOnOverlayGestureListener(new ManagedOverlayGestureDetector.OnOverlayGestureListener() {


            public boolean onZoom(ZoomEvent zoom, ManagedOverlay overlay) {
                return false;
            }


            public boolean onDoubleTap(MotionEvent e, ManagedOverlay overlay, GeoPoint point, ManagedOverlayItem item) {
                Drawable defaultmarker = getResources().getDrawable(R.drawable.marker);     

                ManagedOverlay managedOverlay = overlayManager.createOverlay(defaultmarker);

                //creating some marker:
                managedOverlay.createItem(point);

                //registers the ManagedOverlayer to the MapView
                overlayManager.populate();
                Toast.makeText(getApplicationContext(), "You created a Marker!", Toast.LENGTH_LONG).show();

                return true;
            }


            public void onLongPress(MotionEvent arg0, ManagedOverlay arg1) {
                // TODO Auto-generated method stub

            }


            public void onLongPressFinished(MotionEvent arg0,
                    ManagedOverlay arg1, GeoPoint arg2, ManagedOverlayItem arg3) {
                // TODO Auto-generated method stub

            }


            public boolean onScrolled(MotionEvent arg0, MotionEvent arg1,
                    float arg2, float arg3, ManagedOverlay arg4) {
                // TODO Auto-generated method stub
                return false;
            }


            public boolean onSingleTap(MotionEvent arg0, ManagedOverlay arg1,
                    GeoPoint arg2, ManagedOverlayItem arg3) {
                // TODO Auto-generated method stub
                return false;
            }           
        });
        overlayManager.populate();
    }


    @Override
    protected boolean isRouteDisplayed() {
        return false;
    }
}

それが役に立てば幸い。

于 2010-12-17T05:50:41.563 に答える
0

MyMapViewを使用しようとすると、ClassCastExceptionも発生しました。回避策として、MapViewオブジェクトの代わりにMyMapViewオブジェクトを作成し、プログラムでレイアウトに追加しました。これはうまく機能しました。

    MyMapView mapView = new MyMapView(this, this.getString(R.string.APIMapKey));
    mapView.displayZoomControls(false);
    mapView.setClickable(true);
    FrameLayout item = (FrameLayout) findViewById(R.id.getlocationmap);
    item.addView((MapView)mapView);
于 2012-07-29T02:22:00.880 に答える
0

ハンドラーへの遅延ポスト/メッセージを使用する場合 (私が自分で使用するソリューション)、マップが移動したかどうかをテストすると便利な場合がmapView.getMapCenter()ありmapView.getLatitudeSpan()ますmapView.getLongitudeSpan()

于 2012-08-14T21:16:26.360 に答える