所以我下班之後花了一點時間把之前的寫的範例修改成本次的需求,不過有一點要特別說明的是,之前的圖片縮放法太耗資源了,所以我改成了用 Matrix 的方式去縮放。
以下就是本次的範例說明
Layout 配置
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.google.android.maps.MapView android:id="@+id/mView_Main" android:layout_width="fill_parent" android:layout_height="fill_parent" android:apiKey="你自己的key"> </com.google.android.maps.MapView> </LinearLayout>
跟上次的範例一模一樣。
接下來是我改寫的點位物件,與之前不一樣的是,我拿掉了 getter 跟 setter,變成可以直接存取,然後新增了一個可以直接取得這個物件的位置函式。
package tw.tericky.test; import com.google.android.maps.GeoPoint; public class ImageEntity { public int mLatitude; public int mLongitude; public String mName; public ImageEntity() { mLatitude = 0; mLongitude = 0; mName = ""; } public ImageEntity(int aLatitude, int aLongitude, String aName) { mLatitude = aLatitude; mLongitude = aLongitude; mName = aName; } public GeoPoint getGeoPoint() { return new GeoPoint(mLatitude, mLongitude); } }接著看的是存放點位的Overlay。
package tw.tericky.test; import java.util.ArrayList; import java.util.List; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.view.MotionEvent; import com.google.android.maps.GeoPoint; import com.google.android.maps.ItemizedOverlay; import com.google.android.maps.MapView; import com.google.android.maps.OverlayItem; public class MarkerOverlay extends ItemizedOverlay<overlayitem> { private static final int mTouchMarker = 1000; // 長壓作用時間(毫秒) private static final int mMoveRate = 5; // 地圖移動基數 private static int mOldZoomLevel; // 紀錄地圖的縮放等級 private static long mTouchTime; // 紀錄手指按下的時間 private static PointF mStartPoint; // 紀錄手指按下的坐標 private static int mBaseLevel = 10; // 圖片原大小對應的地圖縮放層級 private static float mLevelRate = 0.2f; // 每一級之間的縮放比例 private static Drawable mMarkerDrawable; // Drawable物件 private static Bitmap mMarkerBmp; // 原本所用的圖示,設成全域變數避免每次重複生成 private List<imageentity> mItems; // 存放點位資料 public MarkerOverlay(Bitmap aDefaultMarker) { super(boundCenterBottom(mMarkerDrawable = new BitmapDrawable(aDefaultMarker))); mItems = new ArrayList<imageentity>(); mMarkerBmp = aDefaultMarker; mStartPoint = new PointF(); mOldZoomLevel = mBaseLevel; // 一定要加,不然當mItems的個數是0個時,會造成crash populate(); } public Boolean addItem(ImageEntity aImageEntity) { Boolean result = aImageEntity != null ? mItems.add(aImageEntity) : false; // 更新OverlayItem前,先把focus移走,不然會出錯 setLastFocusedIndex(-1); // 通知ItemizedOverlay更新OverlayItem populate(); return result; } public Boolean addItems(List<imageentity> aImageEntityList) { Boolean result = aImageEntityList != null ? mItems.addAll(aImageEntityList) : false; // 更新OverlayItem前,先把focus移走,不然會出錯 setLastFocusedIndex(-1); // 通知ItemizedOverlay更新OverlayItem populate(); return result; } public void recycle() { // 回收記憶體,避免造成 memory leak if (!mMarkerBmp.isRecycled()) { mMarkerBmp.recycle(); mMarkerBmp = null; } mMarkerDrawable.setCallback(null); } @Override protected OverlayItem createItem(int aPosition) { OverlayItem item = new OverlayItem(mItems.get(aPosition).getGeoPoint(), mItems.get(aPosition).mName, "Marker"); // 要自己重新設定Market,否則沒有效果 item.setMarker(mMarkerDrawable); return item; } @Override public int size() { return mItems.size(); } public void reSize(int aMapZoomLevel) { // 計算縮放率,記得一定要 > 0 float scale = 1 - (mBaseLevel - aMapZoomLevel) * mLevelRate * 1.1f; if (scale > 0) { Matrix matrix = new Matrix(); matrix.postScale(scale, scale); mMarkerDrawable.setCallback(null); mMarkerDrawable = boundCenterBottom(new BitmapDrawable(Bitmap.createBitmap(mMarkerBmp, 0, 0, mMarkerBmp.getWidth(), mMarkerBmp.getHeight(), matrix, false))); // 更新OverlayItem前,先把focus移走,不然會出錯 setLastFocusedIndex(-1); // 通知ItemizedOverlay更新OverlayItem populate(); } } @Override public boolean onTouchEvent(MotionEvent event, MapView mapView) { try { if (event.getAction() == MotionEvent.ACTION_UP) { float start = mStartPoint.x + mStartPoint.y; float end = event.getX() + event.getY(); int rate = mMoveRate * mapView.getZoomLevel(); // 在手指按壓後,幾乎沒有移動的情況下或是移動量不大的情況下才會觸發事件 if (Math.abs(end - start) < rate) { // 按壓超過設定時間才會觸發 if (event.getEventTime() - mTouchTime > mTouchMarker) { // 取得按壓點轉換成經緯度,放入點位串列中 GeoPoint mPoint = mapView.getProjection().fromPixels((int) event.getX(), (int) event.getY()); this.addItem(new ImageEntity(mPoint.getLatitudeE6(), mPoint.getLongitudeE6(), mPoint.toString())); // 刷新地圖 mapView.invalidate(); } } // 判斷是否有地圖縮放的情況 if (mOldZoomLevel != mapView.getZoomLevel()) { mOldZoomLevel = mapView.getZoomLevel(); this.reSize(mOldZoomLevel); } } else if (event.getAction() == MotionEvent.ACTION_DOWN) { // 紀錄起始按壓位置與時間 mStartPoint.set(event.getX(), event.getY()); mTouchTime = event.getDownTime(); } } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); } return super.onTouchEvent(event, mapView); } }最後是MapActivity的寫法。
package tw.tericky.test; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.os.Bundle; import android.widget.ZoomButtonsController; import android.widget.ZoomButtonsController.OnZoomListener; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapActivity; import com.google.android.maps.MapView; public class AndroidMapTest2Activity extends MapActivity { private MapView mMViewMap; private MarkerOverlay mMarkerOverlay; private OnZoomListener mMapViewOnZoom = new OnZoomListener() { @Override public void onZoom(boolean zoomIn) { if (zoomIn) { mMViewMap.getController().zoomIn(); } else { mMViewMap.getController().zoomOut(); } // 縮放圖標大小 mMarkerOverlay.reSize(mMViewMap.getZoomLevel()); } @Override public void onVisibilityChanged(boolean visible) { } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 原圖尺寸 711 * 384 // 將大圖的尺寸預先縮減 Options opts = new Options(); opts.inSampleSize = 3; Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.qb, opts); // 生成圖層 mMarkerOverlay = new MarkerOverlay(marker); mMViewMap = (MapView) findViewById(R.id.MView_Map); // 加入圖層 mMViewMap.getOverlays().add(mMarkerOverlay); // 刷新 mMViewMap.invalidate(); mMViewMap.setBuiltInZoomControls(true); mMViewMap.setClickable(true); mMViewMap.setEnabled(true); mMViewMap.getController().setZoom(10); ZoomButtonsController zoomButton = mMViewMap.getZoomButtonsController(); zoomButton.setOnZoomListener(mMapViewOnZoom); // zoom 到台灣 mMViewMap.getController().animateTo(new GeoPoint(23949024, 120992340)); } @Override protected void onDestroy() { mMarkerOverlay.recycle(); super.onDestroy(); System.exit(0); } @Override protected boolean isRouteDisplayed() { return false; } }
最後要說的是,為什麼不直接將onTouch事件設給MapView而是設在Overlay裡面,是因為......該死的ZoomButtonsController的Touch事件跟MapView的Touch互衝,所以MapView的Touch只會作用一次而已,這邊害我卡了很久...
按照慣例,附上Source Code(AndroidMapTest2.rar)
有任何疑問歡迎討論!
沒有留言:
張貼留言