Android: MapActivityでダブルタップする

MapActivityを継承したアクティビティで、ダブルタップしても地図がズームインしないので、これはGoogleMapの仕様に合わせたいなぁと試行錯誤した結果を記します。正直、しんどかった。

まぁ、似たようなことを考えている人は世の中にもおられるもので、そこを参考にさせてもらいました。

特定のタップイベント時にMapViewから座標を取得する

まぁ上記のサイトにも書かれているのですが、OnGestureListenerをimplementsしただけでは、MapActivityはタップイベントを拾ってくれません。理由はMapViewが先に拾っちゃうからです。原因がわかったとしても、じゃあどうすればいいか。それがなかなかわからなかったんですが、上記のサイトでヒントを書いてくれてるので、それを参考にします(上記のサイトのは俺がいうのも何だけど、完璧ではなかった…)

下のソースは実際に使ってるものの、処理をかなりパッサリと落として抽出したものです。

public class Place extends MapActivity implements LocationListener,
	OnGestureListener,
	OnDoubleTapListener{
	private boolean mZoom = false;
	private boolean mDoubleTap = false;
	private boolean mSingleTap = true;
	private MapView mMapView;
	private MapController mMapController;
	
    @Override
	protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.place);
        mGDetector = new GestureDetector(this, this);

		mMapView = (MapView)findViewById(R.id.mapview);
		mMapView.setBuiltInZoomControls(true);
		ZoomButtonsController zbc = mMapView.getZoomButtonsController();
		zbc.setOnZoomListener(new OnZoomListener() {
			@Override
			public void onZoom(boolean zoomIn) {
				mZoom = true;
				if(zoomIn){
					mMapController.zoomIn();
				} else {
					mMapController.zoomOut();
				}
			}
			
			@Override
			public void onVisibilityChanged(boolean visible) {
				
			}
		});
		// ...略
	}
	
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		super.dispatchTouchEvent(ev);
		return onTouchEvent(ev);
	}

	@Override
	public boolean onDoubleTap(MotionEvent e) {
		mDoubleTap = true;
		return false;
	}

	@Override
	public boolean onDoubleTapEvent(MotionEvent event) {
		if (mDoubleTap) {
			mDoubleTap = false;
			GeoPoint gp = getGeoPointByPoint((int)event.getX(), (int)event.getY());
			GeoPoint cgp = mMapView.getMapCenter();
			GeoPoint point = new GeoPoint(
					(gp.getLatitudeE6() + cgp.getLatitudeE6()) / 2,
					(gp.getLongitudeE6() + cgp.getLongitudeE6()) / 2);
			if (mMapController.zoomIn()) {
				mMapController.setCenter(point);
			}
		}
		return false;
	}

	@Override
	public boolean onDown(MotionEvent event) {
		mSingleTap = false;
		return false;
	}

	@Override
	public boolean onSingleTapConfirmed(MotionEvent event) {
		mSingleTap = true;
		if (mZoom) {
			// zoomIn, zoomOutしていたら、処理をしない
			mZoom = false;
		} else {
			GeoPoint gp = getGeoPointByPoint((int)event.getX(), (int)event.getY());
			mMapController.animateTo(gp);
		}
		return false;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (mGDetector.onTouchEvent(event)) {
			return true;
		}
		return super.onTouchEvent(event);
	}
	
	private GeoPoint getGeoPointByPoint(int x, int y) {
		Projection projection = mMapView.getProjection();
		return projection.fromPixels(x, y);
	}
}

まず、イベントを全部横取りするために、dispatchTouchEventメソッドを設定します。最初にMapActivityが持っているイベント類を全て実行させるために、super.dispatchTouchEventを実行します。その後、実行したいthis.onTouchEventにイベントを渡します。

this.onTouchEventの中で、GestureDetector.onTouchEventをやっておきます。

重要になるのが、onSingleTapConfirmedメソッド。これは簡単にいうと、シングルタップイベントに該当します。シングルタップで実行したい処理をここに書いてしまいます。これでシングルタップイベントは完成っぽく見えるけど、実は違います。あとで書きますが、罠が潜んでます。

次に、onDoubleTapEvent。これはダブルタップイベントという名前だけあって、そういうイベントなんですが、何故かonDoubleTapメソッドの後に数回コールされてしまうので、onDoubleTap内で現在ダブルタップ中というフラグを立てて、onDoubleTapEventでフラグが経っていたら処理するというふうにします。これで、ダブルタップイベントは完成。

これで、GoogleMapをダブルタップしたら、地図は拡大。シングルタップしたところが画面中央に来てハッピーかと思いきや、なんと、ZoomControlをクリックしたら、シングルタップイベントが走ってしまい、ズームコントロールを押した場所が画面中央に来てしまいます。これはどげんかせんといけませんな。

で、onCreateの中でやっているんですが、ZoomButtonsControllerのインスタンスを生成して、ズームボタンを押したときのイベントを自分で定義します。setOnZoomListenerというやつですね。ここで、onZoomイベントに来たら、現在ズーム処理中のフラグを立てます。あとは拡大・縮小の処理を実行します。

onSingleTapConfirmedメソッドで、ズーム中のフラグが立っていたら、処理をしないようにします。そうすれば、シングルタップOK, ダブルタップOK, ズームコントロールOKになります。ふぅ〜、やれやれだぜ。


タグ Android, Java | パーマリンク.

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です