マップ上への描画処理
ここまでの作業で、表示すべき座標が特定できたので、描画処理を実装しましょう。マップ上に何らかの描画を行う場合は、下記のようなイメージでOverlayというレイヤーを追加していきます。
目的値には画鋲のイメージを使用し、目的地の位置にピンの先端がくるように描画します。
Overlayを継承したDistinationOverlayをMapActivityのインナークラスとして作成し、draw()に描画処理を記述します。描画位置は、現在のマップの表示位置と目的地の座標から計算し、画像のサイズに合わせて描画位置を調整しています。そのままの位置に描画すると、指定したポイントに画像の左上が来るように描画されてしまうので注意してください。
/** * 目的地描画用Overlay */ public class DistinationOverlay extends Overlay { // 描画するイメージ private Bitmap bmp; // 描画する座標 private GeoPoint geoPoint; /** * コンストラクタ * @param bmp 描画するイメージ */ public DistinationOverlay( Bitmap bmp) { this.bmp = bmp; } /** * 描画する座標の取得 * @return 描画する座標 */ public GeoPoint getGeoPoint() { return geoPoint; } /** * 描画する座標の設定 * @param geoPoint 描画する座標 */ public void setGeoPoint(GeoPoint geoPoint) { this.geoPoint = geoPoint; } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { // shadow = true,falseで2回呼び出される if( !shadow){ // Mapの表示位置と座標から画面の描画位置を算出 Projection pro = mapView.getProjection(); Point p = pro.toPixels(geoPoint, null); // 描画(目的地がピンの先に来るようにy座標を調整) int centerX = p.x; int centerY = p.y - bmp.getHeight(); // 画像の描画 canvas.drawBitmap(bmp, centerX, centerY, null); } } }
次に、作成したOverlayをMapViewに設定します。
OverlayはMapView.getOverLays()で取得したList<Overlay>に追加します。 // デフォルトのズーム値 protected int defaultZoom = 17; // 目的地表示Overlay protected DistinationOverlay distLocationOverlay = null; /** * 目的地を設定する * * @param address 住所 */ protected void setDist( Address address){ // AddressからGeoPointに変換 int latitude = (int)(address.getLatitude() * 1E6); int longitude = (int)(address.getLongitude() * 1E6); GeoPoint distPoint = new GeoPoint( latitude, longitude); // 目的地をマップの中心にする。 controller.animateTo( distPoint); // ズームをデフォルト設定 controller.setZoom( defaultZoom); // 目標値の座標にイメージを描画 if( distLocationOverlay == null){ // 初回はOverlayを作成 Bitmap bmp = BitmapFactory.decodeResource( getResources(), R.drawable.pushpin); distLocationOverlay = new DistinationOverlay(bmp); distLocationOverlay.setGeoPoint( distPoint); // Overlayの追加 map.getOverlays().add( distLocationOverlay); } else{ // 2回目以降はOverlayを再利用 distLocationOverlay.setGeoPoint( distPoint); // 無効にして再描画 map.invalidate(); } }
以上で目的地の描画が完成です。
現時点でのソースコードは、次のようになります。
package sample.bizcard; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import sample.bizcard.db.BizCard; import android.app.AlertDialog; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Point; import android.location.Address; import android.location.Geocoder; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast; import android.widget.ToggleButton; import android.widget.AdapterView.OnItemClickListener; import android.widget.RadioGroup.OnCheckedChangeListener; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import com.google.android.maps.Overlay; import com.google.android.maps.Projection; /** * 地図表示アクティビティ * MapViewを扱うアクティビティはcom.google.android.maps.MapActivityを継承する必要がある */ public class MapActivity extends com.google.android.maps.MapActivity implements OnCheckedChangeListener{ // UI部品 private MapView map = null; private RadioGroup radioGroup = null; private RadioButton normalMapRadio = null; private ToggleButton currentLocationToggle = null; // Mapのコントローラ private MapController controller = null; // デフォルトのズーム値 protected int defaultZoom = 17; // 目的地表示Overlay protected DistinationOverlay distLocationOverlay = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.map); // UI部品の取得 map = (MapView) findViewById(R.id.map); normalMapRadio = (RadioButton)findViewById(R.id.normalMapRadio); // デフォルトでマップを選択 normalMapRadio.setChecked( true); radioGroup = (RadioGroup)findViewById( R.id.mapRadioGroup); // 衛星写真選択時のリスナ追加 radioGroup.setOnCheckedChangeListener( this); currentLocationToggle = (ToggleButton)findViewById( R.id.currentLocationToggle); // コントローラの取得 controller = map.getController(); // ZoomControlの利用 map.setBuiltInZoomControls( true); // 衛星写真OFF map.setSatellite( false); map.setClickable(true); map.setEnabled(true); // 座標取得用のGeocoder取得 Geocoder geocoder = new Geocoder( this, Locale.getDefault()); // インテントから住所の文字列を取得 Intent intent = getIntent(); String strAddress = intent.getStringExtra( BizCard.COLUMN_ADDRESS); try { // 住所からAddressへ変換 List<Address> addressList = geocoder.getFromLocationName(strAddress, 10); Address address = null; if( addressList.size() == 1){ address = addressList.get( 0); } else if( addressList.size() != 0){ // 複数のアドレスが見つかった場合はダイアログで対象を選択する AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( this); alertDialogBuilder.setTitle( R.string.select_target); // ダイアログ表示用の文字列の生成 List<String> strAddressList = new ArrayList<String>(); for( Address element: addressList){ int maxAddressLineIdx = element.getMaxAddressLineIndex(); strAddressList.add( element.getAddressLine( maxAddressLineIdx)); } // ダイアログに表示するリストの生成 AddressSelectionListener listener = new AddressSelectionListener( addressList); ListView listView = new ListView( this); ArrayAdapter<String> listAdaptor = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strAddressList); listView.setAdapter(listAdaptor); listView.setOnItemClickListener( listener); // ダイアログにリストを生成 alertDialogBuilder.setView( listView); // ダイアログの表示 AlertDialog dialog = alertDialogBuilder.create(); listener.setDialog( dialog); dialog.show(); } if( address != null){ setDist(address); } } catch (IOException e) { Toast toast = Toast.makeText(this, R.string.cannot_get_address, Toast.LENGTH_SHORT); toast.show(); } } /** * ラジオボタンの選択変更時にマップを切り替える */ public void onCheckedChanged(RadioGroup group, int checkedId) { if( checkedId == R.id.normalMapRadio){ // 衛星写真OFF map.setSatellite( false); } else if( checkedId == R.id.satelliteMapRadio){ // 衛星写真ON map.setSatellite( true); } } /** * 目的地を設定する * * @param address 住所 */ protected void setDist( Address address){ // AddressからGeoPointに変換 int latitude = (int)(address.getLatitude() * 1E6); int longitude = (int)(address.getLongitude() * 1E6); GeoPoint distPoint = new GeoPoint( latitude, longitude); // 目的地をマップの中心にする。 controller.animateTo( distPoint); // ズームをデフォルト設定 controller.setZoom( defaultZoom); // 目標値の座標にイメージを描画 if( distLocationOverlay == null){ // 初回はOverlayを作成 Bitmap bmp = BitmapFactory.decodeResource( getResources(), R.drawable.pushpin); distLocationOverlay = new DistinationOverlay(bmp); distLocationOverlay.setGeoPoint( distPoint); // Overlayの追加 map.getOverlays().add( distLocationOverlay); } else{ // 2回目以降はOverlayを再利用 distLocationOverlay.setGeoPoint( distPoint); // 無効にして再描画 map.invalidate(); } } /** * 目的地描画用Overlay */ public class DistinationOverlay extends Overlay { // 描画するイメージ private Bitmap bmp; // 描画する座標 private GeoPoint geoPoint; /** * コンストラクタ * @param bmp 描画するイメージ */ public DistinationOverlay( Bitmap bmp) { this.bmp = bmp; } /** * 描画する座標の取得 * @return 描画する座標 */ public GeoPoint getGeoPoint() { return geoPoint; } /** * 描画する座標の設定 * @param geoPoint 描画する座標 */ public void setGeoPoint(GeoPoint geoPoint) { this.geoPoint = geoPoint; } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { // shadow = true,falseで2回呼び出される if( !shadow){ // Mapの表示位置と座標から画面の描画位置を算出 Projection pro = mapView.getProjection(); Point p = pro.toPixels(geoPoint, null); // 描画(目的地がピンの先に来るようにy座標を調整) int centerX = p.x; int centerY = p.y - bmp.getHeight(); // 画像の描画 canvas.drawBitmap(bmp, centerX, centerY, null); } } } /** * 住所の候補が複数あった場合にリストで選択された住所を表示するリスナ */ class AddressSelectionListener implements OnItemClickListener{ private List<Address> listAddress = null; private AlertDialog dialog = null; private AddressSelectionListener( List<Address> listAddress){ this.listAddress = listAddress; } public AlertDialog getDialog() { return dialog; } public void setDialog(AlertDialog dialog) { this.dialog = dialog; } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // 選択された住所を目的地として設定 Address address = listAddress.get( position); setDist( address); // ダイアログを閉じる dialog.dismiss(); } } @Override protected boolean isRouteDisplayed() { return false; } }