所以我下班之後花了一點時間把之前的寫的範例修改成本次的需求,不過有一點要特別說明的是,之前的圖片縮放法太耗資源了,所以我改成了用 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)
有任何疑問歡迎討論!



沒有留言:
張貼留言