2011年7月28日 星期四

Android - MapView上面的圖片跟著縮放

昨天在ptt的AndroidDev板看到有鄉民在問怎麼讓MapView上面的圖片跟著縮放,想起之前也有做過類似的測試,就寫了這篇和大家分享。

首先先看到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>

很簡單的一個地圖畫面。



接下來先看一個類別物件,這是我用來存放每一個座標點的資訊。

package tw.tericky.test;

public class ImageEntity {
 private int  mLatitude;
 private int  mLongitude;
 private String mName;

 public ImageEntity(int aLatitude, int aLongitude, String aName) {
  mLatitude = aLatitude;
  mLongitude = aLongitude;
  mName = aName;
 }

 public int getLatitude() {
  return mLatitude;
 }

 public void setLatitude(int aLatitude) {
  mLatitude = aLatitude;
 }

 public int getLongitude() {
  return mLongitude;
 }

 public void setmLongitude(int aLongitude) {
  mLongitude = aLongitude;
 }

 public String getName() {
  return mName;
 }

 public void setName(String aName) {
  mName = aName;
 }

}

再來是關鍵的圖片放置層:

package tw.tericky.test;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

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 ImageOverlay extends ItemizedOverlay<OverlayItem> {
 private static List<ImageEntity> mImageEntityList; // 存放座標點位的串列
 private static int     mBaseLevel = 10; // 圖片原大小對應的地圖縮放層級
 private static float    mLevelRate = 0.1f; // 每一級之間的縮放比例
 private static float    mScale  = 1; // 存放計算後的縮放率
 private static Drawable    mDrawable;   // Drawable物件
 private static Bitmap    mOriginalBmp;  // 原本所用的圖示,設成全域變數避免每次重複生成
 private Context      mContext;   // Context,不知道怎麼解釋

 public ImageOverlay(Context aContext, List<ImageEntity> aImageEntityList, int aBaseLevel, float aLevelRate) {
  // 重點在這裡,把bind的drawable物件先設給mDrawable,再傳入做建構
  super(mDrawable = boundCenterBottom(aContext.getResources().getDrawable(R.drawable.icon)));
  // 設定點位
  setImageList(aImageEntityList);
  mContext = aContext;
  mBaseLevel = aBaseLevel;
  mLevelRate = aLevelRate;
  mOriginalBmp = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.icon);
 }

 @Override
 protected OverlayItem createItem(int i) {
  OverlayItem item = new OverlayItem(new GeoPoint(mImageEntityList.get(i).getLatitude(), mImageEntityList.get(i).getLongitude()), mImageEntityList.get(i).getName(), "Image");
  // 要自己重新設定Market,否則沒有效果
  item.setMarker(mDrawable);
  return item;
 }

 @Override
 public int size() {
  return mImageEntityList.size();
 }

 @Override
 public void draw(Canvas canvas, MapView mapView, boolean shadow) {
  // 計算縮放率,記得一定要 > 0
  float scale = 1 - (mBaseLevel - mapView.getZoomLevel()) * mLevelRate;

  // 當比例不一樣的時候,再做設定,避免無謂的資源浪費
  if (mScale != scale && scale > 0) {
   // 計算縮放後的長寬,一定要 >= 1,因為 < 1,轉成int後,就會是0,這樣就會錯誤
   float scaleWidth = mOriginalBmp.getWidth() * scale;
   float scaleHeight = mOriginalBmp.getHeight() * scale;

   Bitmap newbmp = Bitmap.createScaledBitmap(mOriginalBmp, (int) scaleWidth, (int) scaleHeight, true);
   mDrawable = boundCenterBottom(new BitmapDrawable(mContext.getResources(), newbmp));

   // 更新OverlayItem前,先把focus移走,不然會出錯
   setLastFocusedIndex(-1);
   // 通知ItemizedOverlay更新OverlayItem
   populate();

   // 儲存新的縮放率
   mScale = scale;
  }
  super.draw(canvas, mapView, shadow);
 }

 // 設定點位串列
 public void setImageList(List<ImageEntity> aImageEntityList) {
  if (aImageEntityList == null) {
   aImageEntityList = new ArrayList<ImageEntity>();
  } else {
   mImageEntityList = aImageEntityList;
  }

  setLastFocusedIndex(-1);
  populate();
 }
}

再來看看怎麼呼叫使用:

package tw.tericky.test;

import java.util.ArrayList;
import java.util.List;

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

import android.os.Bundle;

public class AndroidTestActivity extends MapActivity {
 private MapView   mMapView;
 private ImageOverlay mImageOverlay;

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

  // 設定點位位置
  List<ImageEntity> iEntityList = new ArrayList<ImageEntity>();

  iEntityList.add(new ImageEntity(24405053, 120863250, "I1"));
  iEntityList.add(new ImageEntity(24004235, 120341400, "I2"));
  iEntityList.add(new ImageEntity(23398143, 120635284, "I3"));
  iEntityList.add(new ImageEntity(23539228, 121483978, "I4"));
  iEntityList.add(new ImageEntity(24244877, 121445526, "I5"));

  // 生成圖層
  mImageOverlay = new ImageOverlay(AndroidTestActivity.this, iEntityList, 10, 0.1f);

  mMapView = (MapView) findViewById(R.id.mView_Main);
  // 加入圖層
  mMapView.getOverlays().add(mImageOverlay);

  // 刷新
  mMapView.invalidate();

  // 設定有的沒的
  mMapView.setBuiltInZoomControls(true);
  mMapView.setClickable(true);
  mMapView.setEnabled(true);
  mMapView.getController().setZoom(13);

  // zoom 到台灣
  mMapView.getController().animateTo(new GeoPoint(23949024, 120992340));
 }

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

這樣就大功告成了!簡單吧!





程式碼下載:androidTest.rar

延伸閱讀:Android - MapView上面的圖片跟著縮放 Part 2

沒有留言: