package com.android_mvc.framework.ui.view.map; import java.util.HashMap; import java.util.List; import java.util.Timer; import java.util.TimerTask; import com.android_mvc.framework.annotations.SuppressDebugLog; import com.android_mvc.framework.common.FWUtil; import com.android_mvc.framework.gps.LocationUtil; import com.android_mvc.framework.gps.MapLocationListener; import com.android_mvc.framework.ui.view.IFWView; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapView; import com.google.android.maps.MyLocationOverlay; import com.google.android.maps.Overlay; import android.app.Activity; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.view.ViewGroup; /** * MapViewのラッパークラス。 * @author id:language_and_engineering * */ @SuppressDebugLog public class MMapView extends MapView implements IFWView { private Activity activity; // 自分の現在地 private MyLocationOverlay overlay_myloc; private TimerTask task_waitfor_myloc; private int gps_lookup_period = 10 * 1000; private GeoPoint currentGeoPointCache; private MapLocationListener mapLocationListener = null; // アイコン private IconsOverlayImpl overlayImpl; private static final int iconSizeX = 50; private static final int iconSizeY = 50; /** * 初期化 */ public MMapView(Activity context) { // API Keyを使って初期化 super(context, FWUtil.getGoogleMapsAPIKey()); // NOTE: Rはアプリ側のパッケージ名に依存するので,情報を迂回して取得している。 this.activity = context; } /** * 破棄処理 */ public void onActivityDestroy() { if( overlay_myloc != null ) { stopFollowMyLocation(); } } // --------- 自分の現在地の追跡 ---------- /** * 地図の中心地として,GPSの現在地情報を追跡し続ける */ public MMapView followMyLocation() { // マップ上にオーバレイを定義 overlay_myloc = new MyLocationOverlay( activity, this ); overlay_myloc.onProviderEnabled(LocationManager.GPS_PROVIDER); overlay_myloc.enableMyLocation(); // 現在地の追跡を開始 waitForMyLocation(); // このオーバレイをマップ上に追加 getOverlays().add(overlay_myloc); invalidate(); return this; } /** * 現在地情報の追跡を終了する */ public void stopFollowMyLocation() { FWUtil.d("現在地追跡処理を終了"); if( task_waitfor_myloc != null ) { task_waitfor_myloc.cancel(); } if( overlay_myloc != null ) { overlay_myloc.disableMyLocation(); getOverlays().remove(overlay_myloc); } } /** * 位置検出タスクを(再)定義 */ private void defineWaitForMyLocationTask() { task_waitfor_myloc = new TimerTask(){ @Override public void run() { FWUtil.d("現在地の検出処理を開始します。待機中"); // GPSの位置情報を監視 // NOTE: runOnFirstFixは,位置情報が変更してなくても呼ばれる overlay_myloc.runOnFirstFix(new Runnable() { @Override public void run() { onMyLocationFixed(); } }); } }; // cancelしたTimerTaskは再利用不可なので // http://www.02.246.ne.jp/~torutk/javahow2/timer.html#doc1_id122 } /** * 現在地の検出処理を開始 */ private void waitForMyLocation() { // 位置検出タスク defineWaitForMyLocationTask(); // 遅延実行させる // NOTE: そうしないとすぐスタックオーバーフローに new Timer().schedule(task_waitfor_myloc, gps_lookup_period); } /** * 自分の現在地が検出された時の処理。 * 検出された座標に変化がない場合もあるという点に注意。 */ private synchronized void onMyLocationFixed() { final GeoPoint newGeoPoint = overlay_myloc.getMyLocation(); if( newGeoPoint.equals(currentGeoPointCache) ) { FWUtil.d("検出された現在地には変化なし。:" + newGeoPoint.getLatitudeE6() + ", " + newGeoPoint.getLongitudeE6() ); } else { FWUtil.d("新たな現在地を検出:" + newGeoPoint.getLatitudeE6() + ", " + newGeoPoint.getLongitudeE6() ); currentGeoPointCache = newGeoPoint; // 現在のズームレベルを保持 int currentZoomLevel = this.getZoomLevel(); // http://www.anddev.org/map-problems-f26/get-the-current-zoom-level-t15353.html // マップ上で新たな現在位置へ移動 this.getController().animateTo(newGeoPoint); this.getController().setCenter(newGeoPoint); // ズームレベルが勝手に変わってしまうので,もとのレベルに戻す this.zoomLevel(currentZoomLevel); // 取得した現在地をもとにコールバック if( mapLocationListener != null ) { // UIスレッド上で実行する activity.runOnUiThread(new Runnable(){ @Override public void run() { mapLocationListener.onLocationChanged( LocationUtil.geopoint2location(newGeoPoint) ); } }); } } // 監視を継続 waitForMyLocation(); } /** * 現在地検出時のフック処理をセット */ public MMapView onMyLocationChanged( MapLocationListener listener ) { this.mapLocationListener = listener; return this; } /** * 現在地の検出処理の実行間隔をセット */ public MMapView gpsLookupPeriod(int ms) { gps_lookup_period = ms; return this; } // --------- アイコン表示 ---------- /** * アイコン表示用のオーバーレイをセット */ public MMapView setIconsOverlay(IconsOverlaySettings settings) { // デフォルトマーカアイコンの生成 Resources res = this.getResources(); Bitmap bmp_orig = BitmapFactory.decodeResource(res, settings.getIcon()); Bitmap bmp_resized = Bitmap.createScaledBitmap(bmp_orig, iconSizeX, iconSizeY, false); Drawable drawable_icon = new BitmapDrawable(bmp_resized); // オーバーレイを生成 overlayImpl = new IconsOverlayImpl(drawable_icon, this); updateIconsOnOverlay( settings.getOverlayItemsList() ); overlayImpl.addContext(this.activity); // オーバーレイをマップにセット List<Overlay> overlays = this.getOverlays(); overlays.add(overlayImpl); return this; } /** * マップ上にアイコン表示すべきリスト情報を更新 */ public void updateIconsOnOverlay(List<BaseOverlayItem> list ) { overlayImpl.setIconsList( list ); } // --------- Viewの属性操作 ---------- // パラメータ保持 HashMap<String, Object> view_params = new HashMap<String, Object>(); @Override public Object getViewParam(String key) { return view_params.get(key); } @Override public void setViewParam(String key, Object val) { view_params.put(key, val); } // 以下は属性操作 public MMapView widthFillParent() { setViewParam("layout_width", ViewGroup.LayoutParams.FILL_PARENT ); return this; } public MMapView heightFillParent() { setViewParam("layout_height", ViewGroup.LayoutParams.FILL_PARENT ); return this; } // TODO: http://stackoverflow.com/questions/9107957/how-to-set-the-mapviews-width-and-height-in-java-code public MMapView touchable() { setEnabled(true); setClickable(true); // http://d.hatena.ne.jp/language_and_engineering/20110828/p1 return this; } public MMapView showZoomControl() { setBuiltInZoomControls(true); return this; } public MMapView zoomLevel( int level ) { getController().setZoom( level ); // NOTE: ズームレベルは1(広域)~21(細部)まで // https://developers.google.com/maps/documentation/android/reference/com/google/android/maps/MapController#setZoom%28int%29 return this; } /** * ズームレベルを最大にする。 */ public MMapView zoomToMaxDetail() { this.zoomLevel(21); return this; } }