转载:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-gmap/index.html
将 Google Map 控件添加到 GoogleMapExample 工程中
首先,在 AndroidManifest.xml 文件中添加对于 Google Map 库的引用。通过使用 uses-library 标签来添加 google map 库到当前应用中。
清单 1. 添加对于 Google Map 库的引用
<uses-library android:name="com.google.android.maps" />
添加的相应的访问权限:
最终的 AndroidManifest.xml 文件示例如下:
清单 2. AndroidManifest.xml 文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.mapExample" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="3" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <uses-library android:name="com.google.android.maps" /> <activity android:name=".GoogleMapExampleActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
其次,将 MapView 控件添加进布局文件 main.xml 中,并将 1.3 中获得的 API 填入 MapView 控件的声明中。
清单 3. main.xml 文件
<?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/mapView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:clickable="true" android:apiKey="0gtIEXVeu0g96nXpxjvByVW9hs7V0mDiuNHRwRw" /> </LinearLayout>
第三,将 GoogleMapExampleActivity 的继承类修改为 MapActivity,并添加 com.google.android.maps.MapActivity包,重写 isRouteDisplayed 方法,由于示例中没有正在显示的路线信息,这里我们返回 false 即可。
清单 4. GoogleMapExampleActivity 类
public class GoogleMapExampleActivity extends MapActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected boolean isRouteDisplayed() { return false; } }
编译并且运行,将会得到如图 8 所示的地图,这时地图控件已经被我们成功的添加进示例工程中了。
图 8. 世界地图界面
加缩放控件并控制缩放比例,切换地图显示类型(卫星,地图,街道)
Google 地图 MapView 对象自带有控制缩放比例的组件,我们可以通过调用 MapView 对象的 setBuiltInZoomControls方法来完成添加或者移除缩放控件。
1.通过在 layout 文件 main.xml 中的 id 获取找到 MapView 对象
清单 5. 获取 MapView 对象
mapView = (MapView) findViewById(R.id.mapView);
2. 在 mapView 对象上调用 setBuiltInZoomControls 方法,这里传入 boolean 类型的参数,true 和 false 分别表示添加或者移除缩放组件
清单 6. 调用 setBuiltInZoomControls 方法
mapView.setBuiltInZoomControls(true);
需要提到的是,如果添加了缩放控件,它会在第一次触击地图的时候生效,如图 9 所示。
图 9. 地图的缩放控件
通过 MapControl 来控制缩放比例,通过获取 MapView 的 MapControl 对象,控制缩放比例。这里我们设置 16 档为当前缩放大小。
清单 7. 控制缩放比例
mapController = mapView.getController(); mapController.setZoom(16);
Google 地图缩放比例共分为 21 档,具体的缩放划分可以参考 google 文档,以及链接 http://greg-koppel.site88.net/maps/InfoDisplay.html
切换地图的显示类型,Google 地图提供了三种地图的显示方式,分别为卫星,地图,街道,默认设置为街道模式,这些模式的切换可以通过调用 MapView 对象提供的相应方法来完成切换。
最终的代码示例:
清单 8. 切换地图的显示类型
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mapView = (MapView) findViewById(R.id.mapView); // 街道模式(默认设置) mapView.setStreetView(true); // 卫星模式 //mapView.setSatellite(true); // 是否显示交通信息 // mapView.setTraffic(true); mapView.setBuiltInZoomControls(true); mapController = mapView.getController(); mapController.setZoom(16); }
获取当前地理位置信息
Android 系统提供的获取地理位置信息的服务,通过该服务开发者不仅可以方便的获取当前 Android 设备的地理位置信息,并且还提供定制地理位置信息获取的方式,位置变化追踪等。
LocationManager,LocationProviders 这两个类在 Android 系统中,提供获取当前地理位置信息的功能。
下面通过一个具体的示例来说明,如何获取 Android 系统中最后一次更新的地理位置信息。
示例代码如下:
清单 9. 获取 Android 系统中最后一次更新的地理位置信息
mapView = (MapView) findViewById(R.id.mapView); private LocationManager locationManager = null; private String bestProvider = null; private String getBestProvider(Context context){ Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_COARSE); // 设置精度 criteria.setAltitudeRequired(false); // 设置是否需要提供海拔信息 criteria.setBearingRequired(false); // 是否需要方向信息 criteria.setCostAllowed(false); // 设置找到的 Provider 是否允许产生费用 criteria.setPowerRequirement(Criteria.POWER_LOW); // 设置耗电 locationManager=(LocationManager)context.getSystemService(Context.LOCATION_SERVICE); String provider=locationManager.getBestProvider(criteria, true); // 这里可能返回 null, 地理位置信息服务未开启 return provider; } public Location getLastKnowLocation(Context context) { Location ret = null; this.bestProvider = getBestProvider(context); if(this.bestProvider != null) { ret = locationManager.getLastKnownLocation(this.bestProvider); } // 这里可能会返回 null, 表示按照当前的查询条件无法获取系统最后一次更新的地理位置信息 return ret; }
在实际应用中,这里有两个问题需要特别注意:
一方面,当 Android 设备的地理位置信息服务处于未开启的状态时,通过
LocationManager获取的
LocationProvider将会返回
null,在这里需要提示用户当前地理位置信息服务未开启,否则将会有"
j
ava.lang.IllegalArgumentException: provider==null"
异常抛出
(
具体方式请参考
附带
源代码
)
。
另一方面,由于 getLastKnownLocation方法是一个非阻塞调用方法,例如在设置为
GPS方式获取地理位置信息时经常会返回
null,通常处理在 getLastKnownLocation中返回值为
null的方法有两种
:
1.设置地理位置信息获取的精度为粗粒度位置信息获取,相对于
GPS
方式,通过网络信息等方式获取当前位置更容易进行定位。
清单 10. 设置地理位置信息获取的精度
criteria.setAccuracy(Criteria. ACCURACY_COARSE)2. 通过使用 LocationListener 请求并监听用户地理位置信息的变化
Step 1. 创建 Location Listener 对象,实现 LocationListener 接口
Step 2. 使用在 4.1 中初始化的 locationManager 对象请求地理位置信息的更新,并绑定到自己定义的 locationListener 对象
Step 3. 当 LocationManager获取到有效的地理位置信息时,
在 LocationListener 接口定义的 onLocationChanged方法
将会被回调,传入参数就是当前获取的位置。
最后需要注意的是,获取到地理位置信息后,以及当前 Activity 生命周期结束时,将 locationListener 在 LocationManager的绑定移除。
清单 11. 监听用户地理位置信息的变化
private MyLocationListener myLocationListener = null; public class MyLocationListener implements LocationListener { @Override public void onLocationChanged(Location location) { if (location != null) { myLocation = location; mapController.setZoom(16); addMarker(); locationManager.removeUpdates(myLocationListener); } } @Override public void onProviderDisabled(String provider) {} @Override public void onProviderEnabled(String provider) {} @Override public void onStatusChanged(String provider, int status, Bundle extras) {} @Override protected void onPause() { super.onPause(); } @Override protected void onResume() { super.onResume(); myLocation = getLastKnowLocation(this); if(myLocation == null) { if(this.bestProvider != null){ locationManager.requestLocationUpdates(LocationManager. GPS_PROVIDER, 1000, 0, myLocationListener); } } else { addMarker(); } } @Override protected void onDestroy() { super.onDestroy(); locationManager.removeUpdates(myLocationListener); }
在地图上添加当前位置的图标
在地图上添加图标是地图应用时必不可少的功能,Google Map API 也为开发者提供了方便的接口在地图显示图标,本文还是以实例来说明如何将当前位置标示在地图上,效果如图 10 所示。
图 10. 当前位置图标
addMarker
()
方法,
将获取的当前位置信息以图标方式显示在地图上。myMapOverlay
类中
重写 OnTap 方法,该方法会在图标点击事件触发时回调,传入参数为响应触击事件 overlayItem 对象在mItems
中的位置。在我们的示例中,当图标点击事件触发时通过
toast
来显示当前位置信息
private void addMarker() { if (myOverlay == null && myLocation != null) { GeoPoint myGeoPoint = location2GeoPoint(myLocation); if (myGeoPoint != null) { myOverlay = new MyMapOverlay(currLocMaker); myOverlay.setItem(myGeoPoint); mapView.getOverlays().add(myOverlay); mapController.animateTo(myGeoPoint); } } } public class MyMapOverlay extends ItemizedOverlay<OverlayItem> { private List<GeoPoint> mItems = new ArrayList<GeoPoint>(); public MyMapOverlay(Drawable marker) { // 初始化默认图标,并设置其为底部中心对应显示点 super(boundCenterBottom(marker)); } public void setItems(ArrayList<GeoPoint> items) { mItems = items; populate(); } public void setItem(GeoPoint item) { mItems.add(item); populate(); } @Override protected OverlayItem createItem(int i) { // 此处没有进行图标的修改,默认为使用父类的默认图标 return new OverlayItem(mItems.get(i), null, null); } @Override public int size() { return mItems.size(); } @Override protected boolean onTap(int i) { // 图标点击事件处理 Toast.makeText(GoogleMapExampleActivity.this, "当前位置:\n "+ myLocation.getLatitude() + ", "+myLocation.getLongitude(), Toast.LENGTH_SHORT).show(); return true; } }
结束语
本文通过实例介绍如何在 Android 系统上开发以及 Google Map 的简单应用程序,相信读者通过源代码实例以及文字介绍有了大致的了解。接下来我们还会对开发基于 Map 应用过程中一些常用的方法进行汇总,比如,如何自定制图标并添加文字,响应长按事件,zoom 事件,点击图标弹出气球框等。
GoogleMapExampleActivity.java
package com.android.mapexmaple; import java.util.ArrayList; import java.util.List; import com.android.mapExample.R; import com.google.android.maps.GeoPoint; import com.google.android.maps.ItemizedOverlay; import com.google.android.maps.MapActivity; import com.google.android.maps.MapController; import com.google.android.maps.MapView; import com.google.android.maps.OverlayItem; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.widget.Toast; public class GoogleMapExampleActivity extends MapActivity { private MapView mapView = null; private LocationManager locationManager = null; private Location myLocation = null; private MapController mapController = null; private MyLocationListener myLocationListener = null; private String bestProvider = null; private Drawable currLocMaker = null; private MyMapOverlay myOverlay = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); this.mapView = (MapView)findViewById(R.id.mapView); this.mapController = mapView.getController(); this.myLocationListener = new MyLocationListener(); currLocMaker = getResources().getDrawable(R.drawable.marker); mapView.setBuiltInZoomControls(true); mapController.setZoom(16); this.bestProvider = getBestProvider(this); if(this.bestProvider == null){ AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("当前地理位置服务为禁用状态,启动该服务?") .setCancelable(false) .setPositiveButton("是的", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Intent gpsOptionsIntent = new Intent( android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(gpsOptionsIntent); } }) .setNegativeButton("算了", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); } } private String getBestProvider(Context context){ Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_COARSE); // 设置精度 criteria.setAltitudeRequired(false); // 设置是否需要提供海拔信息 criteria.setBearingRequired(false); // 是否需要方向信息 criteria.setCostAllowed(false); // 设置找到的Provider是否允许产生费用 criteria.setPowerRequirement(Criteria.POWER_LOW); // 设置耗电 locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); String provider = locationManager.getBestProvider(criteria, true); //这里可能返回null return provider; } public Location getLastKnowLocation(Context context) { Location ret = null; this.bestProvider = getBestProvider(context); if(this.bestProvider != null) { ret = locationManager.getLastKnownLocation(this.bestProvider); } // 这里可能会返回null, 表示按照当前的查询条件无法获取系统最后一次更新的地理位置信息 return ret; } public class MyLocationListener implements LocationListener { public void onLocationChanged(Location location) { if (location != null) { myLocation = location; mapController.setZoom(16); addMarker(); locationManager.removeUpdates(myLocationListener); } } public void onProviderDisabled(String provider) { } public void onProviderEnabled(String provider) { } public void onStatusChanged(String provider, int status, Bundle extras) { } } @Override protected void onPause() { super.onPause(); } @Override protected void onResume() { super.onResume(); myLocation = getLastKnowLocation(this); if(myLocation == null) { if(this.bestProvider != null){ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, myLocationListener); } } else { addMarker(); } } @Override protected boolean isRouteDisplayed() { return false; } @Override protected void onDestroy() { super.onDestroy(); locationManager.removeUpdates(myLocationListener); } /* * Add marker to my current location */ private void addMarker() { if (myOverlay == null && myLocation != null) { GeoPoint myGeoPoint = location2GeoPoint(myLocation); if (myGeoPoint != null) { myOverlay = new MyMapOverlay(currLocMaker); myOverlay.setItem(myGeoPoint); mapView.getOverlays().add(myOverlay); mapController.animateTo(myGeoPoint); } } } private GeoPoint location2GeoPoint(Location location){ if (location != null) { return new GeoPoint((int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6)); } else { return null; } } /** * Customized map overlay. */ public class MyMapOverlay extends ItemizedOverlay<OverlayItem> { private List<GeoPoint> mItems = new ArrayList<GeoPoint>(); public MyMapOverlay(Drawable marker) { super(boundCenterBottom(marker)); } public void setItems(ArrayList<GeoPoint> items) { mItems = items; populate(); } public void setItem(GeoPoint item) { mItems.add(item); populate(); } @Override protected OverlayItem createItem(int i) { return new OverlayItem(mItems.get(i), null, null); } @Override public int size() { return mItems.size(); } @Override protected boolean onTap(int i) { //图标点击事件处理 Toast.makeText(GoogleMapExampleActivity.this, "当前位置:\n "+ myLocation.getLatitude() + ", " +myLocation.getLongitude(), Toast.LENGTH_SHORT).show(); return true; } } }
marker.png