学习内容
Google Map服务简介
获取Google Map API Key
根据 GPS信息在地图上定位
根据GPS信息地图上跟踪用户轨迹
调用Google的地址解析服务
根据地址在地图上定位
能力目标
了解Google Map服务
掌握获取Google Map API Key的方法
能够根据 GPS信息在地图上定位
能够根据GPS信息地图上跟踪用户轨迹
掌握调用Google的地址解析服务
熟练根据地址在地图上定位
本章简介
上一章介绍了如何使用Android的GPS来获取设备的定位信息,但这种方式得到的定位信息只不过是一些数字的经度、纬度值,如果这些经度、纬度值不能以更加形象、直观的方式显示出来,对于大部分普通用户而言,这些数据几乎没有任何价值。要想让这些经纬度值“派上”用场,就需要用到本章中介绍到的Google Map服务。在本章中我们首先对Google Map进行简单的介绍,然后介绍Android中进行Google Map开发需要的准备工作,最后通过一系列的案例讲解了在Android中进行Google Map开发的方法及技巧。
核心技能部分
谷歌地图(Google Map)是 Google 公司提供的电子地图服务,包括局部详细的卫星照片。它提供了三种视图:一是矢量地图(传统地图),可提供政区和交通以及商业信息;二是不同分辨率的卫星照片(俯视图);三是地形视图,可以用以显示地形和等高线。
Google公司在2004年11月收购了美国Keyhole公司,推出了http://maps.google.com,令人耳目一新。但Google并未就此止步,在2005年6月底推出了桌面工具Google Earth,把“地球”放到了每个人的桌面上,让你坐在电脑前,就可以在名川大山间漫步,在摩天楼群中俯瞰,这一软件迅速震惊了整个互联网界。随着Google Map和Google Earth的诞生,也出现了很多非常有趣的应用,这些基于Google Earth和Google Maps的小游戏非常有创意,吸引了不少玩家。
目前Google Map在美国、英国、加拿大、日本等其它地区的服务已经完成。在北美的部分地方,开通了“街景视图”服务。使用者可以通过街道上的视角查看街景(例如公交车站,商铺等)。可以让使用者搜寻餐厅,亦可以建立自己的地图,与其他使用者分享有关的照片和评论。其它功能包括路线查询,可以显示两个地点之间的距离和行车时间等。
对于Google重点推出的Android系统来说,Google Map服务在其中有大量的应用。Android中基于Google Map的服务体现在两个方面:地图API和位置API。这两个API是彼此隔离的,都拥有自己的包。地图包为com.google.android.maps,而位置包为android.location。Android中的地图API提供了一些工具来显示和操作地图。例如:可以缩放和平移地图,可以更改地图模式(例如,从卫星视图更改为街道视图),可以向地图添加自定义数据等等;位置API则提供了GPS数据和实时位置数据的处理。这些API通过互联网调用来自Google服务器的服务,因此它们需要有网络连接才能工作。此外,在使用这些Android Maps API服务开发应用程序之前,需要同意Google的服务条款。Google对服务数据的用途设置了一定的限制,例如,可以将位置信息用于用户的个人用途,不能用于某些商业用途。
要使用Google Map给Android应用程序加上强大的地图功能,就得使用SDK的扩展库com.google.android.maps。这个库不是标准的Android SDK内容,需要单独下载,默认下载后的位置是在“sdk根目录\add-ons\addon_google_apis_google_inc_10\libs”下面。
而在Android中要开发基于地图的应用,使用的类是MapView,如果要讲Google Map数据显示到MapView上,必须注册Google Map服务,并获得一个Maps API Keys。
Android系统要求所有应用程序都必须使用证书进行签名。证书里面包含一个唯一的key,它用于标识应用程序的作者,其实就是MD5。在开发和调试的过程当中可以使用Debug版本的证书。只有使用了Map API Key,android才能使用Google Maps服务,获得地图数据。
注意:
如果开发一个使用GoogleMaps服务的程序,需要两个密钥:一个密钥用于使用模拟器进行开发,一个用于生产用途。这是因为在开发和生产用途上所用于获取Map API Key的证书是不同的。当然,在开发阶段只需要得到开发时候的密钥就可以了。至于生产用途的密钥,和开发密钥的获得方法很相似,所不同的是生产用途的密钥必须基于android安全性。
获取 Map API Key的步骤如下:
1、 找到debug.keystore文件
在开发环境Eclipse下,依次打开Windows—>Preferences—>Android—>Build,其中Default debug keystore的值便是debug.keystore的路径。当我们在Eclipse中运行Android程序时,默认就会使用debug.keystore进行签名。如下图10.1.1所示。
图10.1.1 获取 keystore
2、 提取debug.keystore的MD5值:
首先在命令行提示符下执行命令“keytool -list -keystore debug.keystore”(这里的debug.keystore的路径要写完整),回车后,会提示我们输入密码,接着输入默认的密码“android”,再次回车,即可取得MD5值。如下图10.1.2所示。
图10.1.2 获取 MD5值
3、 申请Google Maps API Key
打开网址http://code.google.com/intl/zh-CN/android/maps-api-signup.html。在打开的页面中选中“I have read and agree with the terms and conditions”复选框,在“My certificate's MD5 fingerprint”后的文本输入框中输入刚才得到的MD5值,最后按下“Generate API Key”按钮,就可得到我们所要申请的API Key。如下图10.1.3和图10.1.4所示。
图10.1.3 API Key申请页面
图10.1.4申请到的API Key
到这里我们就完成了Android Map API Key的申请,记下图10.1.4中申请到的的API Key值,在我们的程序中会用到。
注意:
在获取API Key的时候需要输入账号和密码,大家可以直接使用Google邮箱的就行,如果没有的话,可以去免费注册一个。
申请完Map API Key之后,还需要创建一个能够运行Google地图的模拟器,该模拟器的创建步骤与前文中模拟器的创建稍有不同,具体就是在选择模拟器的Target时,选择的是Google APIs,而不再是Android 2.2了,因为该版本提供了对Google Map服务的支持。如下图10.1.5所示。
图10.1.5 创建AVD
完成上述一系列步骤之后,我们就可以正式开始google地图应用程序的开发了,
在com.google.android.maps包里面定义了一系列用于在Google地图上显示、控制等相关的功能类。其中最主要的有以下几个类:
Ø MapActivity
它是Map专用的Activity,该类是专门用于显示Google Map的Activity抽象类,任何想要显示MapView的activity都需要派生自MapActivity,并且在其派生类的onCreate()中都要创建一个MapView实例,此处还必须得重写它的isRouteDisplayed()方法。该类常用的方法如表10-1-1所示。
表10-1-1 MapActivity常用方法
方法 |
说明 |
protected abstract boolean isRouteDisplayed() |
确定是否显示路线地图。 |
protected boolean isLocationDisplayed() |
确定是否显示位置信息。 |
Ø MapView
MapView用于显示地图,它继承自android.view.ViewGroup。因为MapView需要通过后台的线程连接网络或者文件系统,而这些线程要由MapActivity来管理,因此MapView必须和MapActivity配合使用,且只能被MapActivity创建。Android中的许多地图技术都依赖于MapActivity和 MapView的扩展。这两个类必须协同工作才能完成Google地图的相关操作。MapView类常用的方法如表10-1-2所示。
表10-1-2 MapView常用方法
方法 |
说明 |
public void setBuiltInZoomControls(boolean on) |
设置是否启用内置的放大缩小功能 |
public final util.List |
获取当前MapView上的所有的层组成的列表 |
MapController MapView.getController() |
返回当前Map的控制器。 |
void MapView.setSatellite(boolean on) |
设置为卫星模式 |
void MapView.setTraffic(boolean on) |
设置为交通模式 |
void MapView.displayZoomControls(boolean takeFocus) |
设置缩放控制 |
Projection MapView.getProjection() |
得到一个投影,用来在屏幕坐标和经纬度之间转换。 |
Point Projection.toPixels(GeoPoint in,Point out) |
将经纬度转换成屏幕上的坐标。 |
Ø MapController
它是Map控制器,它用于控制地图的旋转、缩放等。该类常用的方法如表10-1-3所示。
表10-1-3 MapController常用方法
方法 |
说明 |
public int setZoom(int zoomLevel) |
设置观看地图的高度,即设置地图放大缩小的倍数(1-21) |
public boolean zoomIn() |
缩小地图 |
public boolean zoomOut() |
放大地图 |
void MapController.animateTo(GeoPoint point) |
定位到指定的坐标 |
Ø Overlay
该抽象类表示一个可显示在地图之上的可绘制的对象或层,比如在地图上标注一些文字或图标信息等。OverLay本身是完全透明的,它是一个地图标记的容器,即在OverLay当中可以加入各种各样的标记。一个OverLay对象代表了显示在MapView之上的图层。在一个OverLay当中可以包含多个地图标记。我们需要实现Overlay类中的draw()方法才能在地图上绘制信息。
Ø GeoPoint
这是一个通过经纬度表示位置的对象。它提供了如下表10-1-4所示的常用的构造方法及普通方法。
表10-1-4 GeoPoint常用方法
方法 |
说明 |
public GeoPoint(int latitudeE6,int longitudeE6) public Geocoder(Context context, Locale locale) |
构造方法 |
public int getLatitudeE6() |
获得纬度(latitude ) |
public int getLongitudeE6() |
获取经度(longitude) |
在Android应用中调用Google Map服务主要依赖于MapView、MapController、GeoPoint这三个API,掌握了它们的用法之后,接下来就可以在Android开发和Map相关的应用了。
示例10.1
在地图当中通过添加标记Marker的方式标注指定位置。
在使用google地图时,可以看到地图上常用图钉或小气球来标记位置。Google Maps 是通过在地图上添加层,然后再在这个图层上面添加标记来实现此功能的。Android提供了多个类来实现在地图上添加层。实现此类功能的一个关键类是Overlay,也可以使用此类的扩展类ItemizedOverlay。
在地图当中使用标记的步骤为:
(1) 在MapView之上创建一个单独的图层(一个MapView上可以添加很多图层);
(2) 创建标记对象;
(3) 将标记显示在指定图层的指定位置;
(4) 处理点击标记的事件;
Activity类的代码如下:
public class UseOverlayItemActivity extends MapActivity{
private MapView mapView ;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.useoverlayitem);
mapView = (MapView) findViewById(R.id.mapView);
//设置MapView显示时,用于缩放的工具条
mapView.setBuiltInZoomControls(true);
Drawable drawable = getResources().getDrawable(R.drawable.tool);
FirstOverlay firstOverlay = new FirstOverlay(drawable , this);
//GeoPoint通过经纬度指定地图上的点:二七广场
GeoPoint point = new GeoPoint((int)113.6663284*1000000, (int)34.7520144*1000000);
//创建图层上的一个对象
OverlayItem overlayItem = new OverlayItem(point, "你好", "我现在是在哪儿呀");
firstOverlay.addOverlay(overlayItem);
List
overlays.add(firstOverlay);
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
class FirstOverlay extends ItemizedOverlay
private Context context;//当弹出对话框时使用
//用来保存该图层当中所有的标记对象
private ArrayList
//第一个参数用于指定标记所使用的默认图片,第二个参数供对话框显示时使用
public FirstOverlay(Drawable defaultMarker , Context context) {
//boundCenterBottom()将具体显示的点置于图片下方的正中央,即设置转图片的位置
super(boundCenterBottom(defaultMarker));
this.context = context;
}
public FirstOverlay(Drawable defaultMarker) {
super(boundCenterBottom(defaultMarker));
}
//用于将生成好的OverlayItem对象添加到List当中
public void addOverlay(OverlayItem overlayItem) {
overlayItems.add(overlayItem);
populate();//为添加进来的OverlayItem执行所有的操作。
}
@Override
protected OverlayItem createItem(int i) {
//用于创建一个OverlayItem对象
return overlayItems.get(i);
}
@Override
public int size() {
//返回当前OverLay当中所包含的OverlayItem对象的个数
return overlayItems.size();
}
//当用户点击标记时触发的操作
@Override
protected boolean onTap(int index) {
OverlayItem item = overlayItems.get(index);
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(item.getTitle());
builder.setMessage(item.getSnippet());
Dialog dialog = builder.create();
dialog.show();
return true;
}
}
}
本示例中用到了ItemizedOverlay类,ItemizedOverlay是OverLay的子类,在该类当中包含一个或者多个OverlayItem。还用到了OverlayItem类,一个OverlayItem对象代表地图上的一个标记。
程序执行的时候,在ItemizedOverlay类的内部,会调用size()方法来确定覆盖项的数量,然后进入循环,为每个项调用createItem(i)。
Drawable ItemizedOverlay.boundCenterBottom(Drawable marker)将具体显示的点置于图片下方的正中央,即设置图片的位置。
本程序的运行效果如下图10.1.6所示。
图10.1.6 地图定位效果
前面介绍的在地图上定位的例子需要用户输入定位点的经度、纬度才能进行定位,这种方式显然不太现实,对于普通用户来说,根据地址进行定位才是更有实用价值的。
由于Google Map的地图定位必须根据经纬度来完成,因此如果需要让程序根据地址进行定位,则需要先把地址解析成经纬度值。这里面涉及到如下两个概念:
Ø 地理解析:将地址字符串转换为经纬度。
Ø 反向地理解析:将经纬度转换为地址字符串。
Android为地址解析提供了Geocoder地理解析器工具类,该工具类用于处理地理解析和反向地理解析,它主要用来提供Geocoding服务。该类提供了如下两个方法来进行地址的解析与反向地址解析。
Ø public List<Address> Geocoder.getFromLocation(double latitude, double longitude, int maxResults)
执行地址解析,把经度、纬度值转换成为字符串形式的地址值。
Ø public List
getFromLocationName(String locationName , int maxResults)查询具体地址对应的经纬度。返回值是一个地址列表。
locationName:位置名称。
maxResults:最多返回多少条结果。
虽然Geocoder工具类提供了上面两个方法来进行地址解析和反向地址解析,但实际上这个类还是需要调用网络上的Google服务。原因是,Android平台不可能将地球上所有地名与经纬度之间的映射关系都存放在手机中。
尽管Android的API文档中给出 了Geocoder工具类的说明,而且也可以在程序中使用Geocoder,但从Android2.2开始,Geocoder类在模拟器中就无法使用,这是Android内核编译时出现的bug,我们暂时无法解决。强行运行的话,会报出Service not Available的警告。虽然利用Geocoder类无法对地址进行解析和反解析,但好在Google已经将地址解析、反向解析的API公开出来了,用户登录https://developers.google.com/maps/documentation/geocoding/?hl=zh-CN 站点即可看到地址解析、反向解析相关API的详细说明。以二七区为例,地址解析相关页面的地址为:http://maps.google.com/maps/api/geocode/json?address=erqiqu&sensor=false。反向地址解析的服务地址为:http://maps.google.com/maps/api/geocode/json?latlng=34.752014421190424,113.66632841527462&sensor=false。
Android中进行地址解析和反向地址解析的步骤为:
(1) 通过HttpClient或HttpURLConnection向指定的地址发送请求。
(2) 解析服务响应数据,获取解析结果。
示例10.2
演示地理位置的解析与反解析。
本示例程序最终运行结果如下图10.1.7所示。本程序中用户可以手工输入待解析的地址字符串,或待反向解析的经纬度。当用户单击按钮时会进行相应的的解析和反向解析。
图10.1.7 程序界面
为了实现本程序,我们先提供一个具体实现解析与反解析的Activity类,详细代码如下:
public class ParseUtil {
// 根据地址获取对应的经纬度
public static double[] getLocationInfo(String address) {
// 定义一个HttpClient,用于向指定地址发送请求
HttpClient client = new DefaultHttpClient();
// 向指定地址发送GET请求
HttpGet httpGet =
new HttpGet("http://maps.google.com/maps/api/geocode/json?address="
+ address + "ka&sensor=false");
StringBuilder sb = new StringBuilder();
try {
// 获取服务器的响应
HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
// 获取服务器响应的输入流
InputStream stream = entity.getContent();
// 循环读取服务器响应
while ((int b = stream.read()) != -1) {
sb.append((char) b);
}
// 将服务器返回的字符串转换为JSONObject对象
JSONObject jsonObject = new JSONObject(sb.toString());
// 从JSONObject对象中取出代表位置的location属性
JSONObject location = jsonObject.getJSONArray("results")
.getJSONObject(0)
.getJSONObject("geometry")
.getJSONObject("location");
// 获取经度信息
double longitude = location.getDouble("lng");
// 获取纬度信息
double latitude = location.getDouble("lat");
// 将经度、纬度信息组成double[]数组
return new double[] { longitude, latitude };
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 根据经纬度获取对应的地址
public static String getAddress(double longitude, double latitude) {
// 定义一个HttpClient,用于向指定地址发送请求
HttpClient client = new DefaultHttpClient();
// 向指定地址发送GET请求
HttpGet httpGet =
new HttpGet("http://maps.google.com/maps/api/geocode/json?latlng="
+ latitude + "," + longitude + "&sensor=false");
StringBuilder sb = new StringBuilder();
try {
// 执行请求
HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
// 获取服务器响应的字符串
InputStream stream = entity.getContent();
while ((int b = stream.read()) != -1) {
sb.append((char) b);
}
// 把服务器相应的字符串转换为JSONObject
JSONObject jsonObj = new JSONObject(sb.toString());
// 解析出响应结果中的地址数据
return jsonObj.getJSONArray("results")
.getJSONObject(0)
.getString("formatted_address");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
再提供一个Activity类,在该类中实现对上面工具类方法的调用,该Activity类的详细代码如下:
public class GeocoderParseActivity extends Activity {
Button parseBtn, reverseBtn;
EditText etLng, etLat, etAddress;
TextView etResult;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geocoderparse);
// 获取界面中的可视化组件
etLng = (EditText) findViewById(R.id.lng);
etLat = (EditText) findViewById(R.id.lat);
etAddress = (EditText) findViewById(R.id.address);
etResult = (TextView) findViewById(R.id.result);
parseBtn = (Button) findViewById(R.id.parse);
parseBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String address = etAddress.getText().toString().trim();
if (address.equals("")) {
Toast.makeText(GeocoderParseActivity.this,
"请输入有效的地址",
Toast.LENGTH_LONG).show();
} else {
double[] results = ParseUtil.getLocationInfo(address);
etResult.setText(address + "的经度是:"
+ results[0] + "\n纬度是:"
+ results[1]);
}
}
});
reverseBtn = (Button) findViewById(R.id.reverse);
reverseBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String lng = etLng.getText().toString().trim();
String lat = etLat.getText().toString().trim();
if (lng.equals("") || lat.equals("")) {
Toast.makeText(GeocoderParseActivity.this,
"请输入有效的经度、纬度!",
Toast.LENGTH_LONG).show();
} else {
String result = ParseUtil.getAddress(Double.parseDouble(lng),
Double.parseDouble(lat));
etResult.setText("经度:" + lng + "、纬度:" + lat
+ "的地址为:\n" + result);
}
}
});
}
}
程序运行结果如下图10.1.8和图10.1.9所示。
图10.1.8 解析结果
图10.1.9 反向解析结果
实际上Android应用可以通过GPS来获取定位信息。上一章中我们已经介绍了如何通过GPS来获取定位信息,因此如果把前面介绍的GPS定位与本章中Google Map结合起来,就可以非常方便地开发出GPS导航应用。
要进行GPS导航的开发,通常需要用到以下几个类:
该类提供Android的用户定位相关的服务,通过LocationManager我们可以获得一个位置提供者的列表。
LocationManager服务是一项系统级服务,它提供了获得设备地理位置、在设备进入指定地理位置时(通过Intent)通知用户等功能。LocationManager服务使用Location provider来提取详细的地理位置信息。目前,有两种类型的位置提供程序:GPS和网络。GPS为程序提供使用全球定位系统获取位置信息的功能,而网络为程序提供使用手机信号塔或wifi网络获取位置信息的功能。
LocationManager类提供了以下重要的方法
Ø void requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)
为LocationManager绑定一个监听器。为LocationManager注册一个周期性的更新视图。
provider:定义当前所使用的LocationProvider,即定义服务的提供者。
minTime:隔多长时间更新一次新户的位置,建议60秒。
minDistance:两次定位之间的最短距离。
listener :LocationListener监听器,用来监听定位信息的改变。
Ø String LocationManager.getBestProvider(Criteria criteria, boolean enabledOnly)
返回和给定的criteria最匹配的provider的名字。当enabledOnly取值为false,不管当前的Provider是否处于可用状态,都会查询出来;取值为true,只在已经打开Provider中去查找最合适的。
Ø List
以列表的形式返回当前设备支持的所有已知的LocationProvider的名称。
Ø Location LocationManager.getLastKnownLocation(String provider)
获取设备最后的已知位置。
该接口提供了定位信息发生改变时的回调函数。使用它之前必须事先在定位管理器中注册监听器对象,注册监听器对象使用LocationManager.requestLocationUpdates()方法。
这个接口的实现类必须实现的方法有:
Ø public void onStatusChanged(String provider, int status, Bundle extras)
Provider的状态在可用、暂时不可用和无服务三个状态之间切换时触发此函数
Ø public void onProviderEnabled(String provider)
Provider启用时触发此函数。比如GPS打开。
Ø public void onProviderDisabled(String provider)
Provider禁用时触发此函数。比如GPS关闭。
Ø public void onLocationChanged(Location location)
当用户的位置发生改变时触发此函数。
示例10.3
开发一个非常简单的GPS导航系统,要求该应用程序每隔30秒获取一次GPS定位数据,当程序得到 GPS定位信息之后,就把Google Map定位到该位置,这样就可以在地图上实时地跟踪设备的移动位置。
本程序的界面非常简单,只提供一个MapView来显示设备在地图上的位置。我们重点来看一下Activity类的代码:
public class GPSNavigation extends MapActivity {
MapView mv;
MapController controller;
Bitmap posBitmap;
LocationManager locManager;
@Override
protected void onCreate(Bundle status) {
super.onCreate(status);
setContentView(R.layout.gps);
posBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.tool);
// 获得界面上MapView对象
mv = (MapView) findViewById(R.id.mapView);
// 设置显示放大、缩小的按钮
mv.setBuiltInZoomControls(true);
// 创建MapController对象
controller = mv.getController();
// 获取LocationManager对象
locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// 设置每30秒获取一次GPS的定位信息
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
30000, 10, new LocationListener() {
@Override
public void onLocationChanged(Location location) {
// 当GPS定位信息发生改变时,更新位置
updateMapView(location);
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
// 当GPS LocationProvider可用时,更新位置
updateMapView(locManager.getLastKnownLocation(provider));
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
});
}
@Override
protected boolean isRouteDisplayed() {
return true;
}
// 根据Location来更新MapView
private void updateMapView(Location location) {
// 将Location对象中的经、纬度信息包装成GeoPoint对象
GeoPoint gp = new GeoPoint((int) (location.getLatitude() * 1E6),
(int) (location.getLongitude() * 1E6));
// 设置显示放大缩小按钮
mv.displayZoomControls(true);
// 将地图移动到指定的地理位置
controller.animateTo(gp);
// 获得MapView上原有的Overlay对象
List
// 清除原有的Overlay对象
ol.clear();
// 添加一个新的OverLay对象
ol.add(new PosOverLay(gp, posBitmap));
}
}
class PosOverLay extends Overlay {
// 定义该PosOverLay所绘制的位图
Bitmap posBitmap;
// 定义该PosOverLay绘制位图的位置
GeoPoint gp;
public PosOverLay(GeoPoint gp, Bitmap posBitmap) {
super();
this.gp = gp;
this.posBitmap = posBitmap;
}
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
if (!shadow) {
// 获取MapView的Projection对象
Projection proj = mapView.getProjection();
Point p = new Point();
// 将真实的地理坐标转化为屏幕上的坐标
proj.toPixels(gp, p);
// 在指定位置绘制图片
canvas.drawBitmap(posBitmap, p.x - posBitmap.getWidth() / 2,
p.y - posBitmap.getHeight(), null);
}
}
}
因为本程序需要使用Google Map,因此需要访问网络和GPS定位的权限,在功能清单文件中添加如下权限:
程序运行结果略。
任务实训部分
训练技能点
Ø Overlay的使用
Ø MapView的使用
Ø MapController的使用
需求说明
使用Overlay 实现示例10.1的在地图当中指定位置添加标记的功能,要求程序会根据用户手工输入指定的经纬度进行定位。
核心代码
public class UseTagActivity extends MapActivity {
private MapView mapView;
private MapController controller;
private GeoPoint geoPoint;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.useoverlayitem);
mapView = (MapView) findViewById(R.id.mapView);
// 设置为交通模式
// mMapView.setTraffic(true);
// 设置为卫星模式
mapView.setSatellite(true);
// 设置为街景模式
// mapView.setStreetView(false);
// 取得MapController对象(控制MapView)
controller = mapView.getController();
mapView.setEnabled(true);
mapView.setClickable(true);
// 设置地图支持缩放
mapView.setBuiltInZoomControls(true);
// 设置起点为二七
geoPoint = new GeoPoint((int)34.752014421190424*1000000,
(int)113.66632941527462*1000000);
controller.animateTo(geoPoint);// 定位到二七
controller.setZoom(13);// 设置倍数(1-21)
// 添加Overlay,用于显示标注信息
MyLocationOverlay myLocationOverlay = new MyLocationOverlay();
List
list.add(myLocationOverlay);
}
protected boolean isRouteDisplayed() {
return false;
}
class MyLocationOverlay extends Overlay {
@Override
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when) {
super.draw(canvas, mapView, shadow);
Paint paint = new Paint();
Point myScreenCoords = new Point();
// 将经纬度转换成实际屏幕坐标
mapView.getProjection().toPixels(geoPoint, myScreenCoords);
paint.setStrokeWidth(1);
paint.setARGB(255, 255, 0, 0);
paint.setStyle(Paint.Style.STROKE);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.tool);
canvas.drawBitmap(bmp, myScreenCoords.x, myScreenCoords.y, paint);
canvas.drawText("二七广场", myScreenCoords.x, myScreenCoords.y, paint);
return true;
}
}
}
训练技能点
Ø Overlay
Ø MapView
Ø MapController
Ø Projection
需求说明
在使用公交查询系统时,它不仅会将符合条件的线路以文本的形式显示出来,还会以绘图的形式在地图上显示出来,非常直观方便,如下图10.2.1所示。本示例中要实现的功能就是在地图上两个地点之间绘制出公交车的运行路线。
图10.2.1 公交路线图
实现思路
核心代码如下所示:
public class DrawLineActivity extends MapActivity {
private List
private MapController controller;//主要用于对地图进行控制
private GeoPoint start;
private GeoPoint end;
private Projection projection;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.drawline);
//指定起始和终止位置的经纬度
start = new GeoPoint(19240000, -99120000);
end = new GeoPoint(19340000, -99220000);
MapView mapView = (MapView) findViewById(R.id.mapView);
mapView.setBuiltInZoomControls(true);
//mapView.setStreetView(true);过时
controller = mapView.getController();
overlays = mapView.getOverlays();
projection = mapView.getProjection();
overlays.add(new PointOverlay(start));
overlays.add(new PointOverlay(end));
overlays.add(new LineOverlay(start ,end));
controller.animateTo(end);//利用动画的方式将地图定位到指定的位置
controller.setZoom(12);//设置地图放大的级别
}
@Override
protected boolean isRouteDisplayed() {
return false;
}
//这个类的对象用于在地图上绘制图标
class PointOverlay extends Overlay {
private GeoPoint geoPoint;
public PointOverlay(){
}
public PointOverlay(GeoPoint point){
this.geoPoint = point;
}
public void draw(Canvas canvas , MapView mapView , boolean shadow) {
super.draw(canvas, mapView, shadow);
Point point = new Point();
projection.toPixels(geoPoint, point);
Bitmap bmp = BitmapFactory.decodeResource(getResources(),
R.drawable.tool);
Paint paint = new Paint();//画笔
//在画布上绘制一个位图,此处之所以减16,是图片的高度为16
canvas.drawBitmap(bmp, point.x, point.y -16 ,paint);
}
}
//这个类的对象用于在地图上绘制线条
class LineOverlay extends Overlay {
private GeoPoint start;
private GeoPoint end;
public LineOverlay( ){
}
public LineOverlay(GeoPoint start , GeoPoint end){
this.start = start;
this.end = end;
}
public void draw(Canvas canvas , MapView mapView , boolean shadow) {
super.draw(canvas, mapView, shadow);
Paint paint = new Paint();//画笔
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeWidth(2);
Point startPoint = new Point();
Point endPoint = new Point();
projection.toPixels(start, startPoint);
projection.toPixels(end, endPoint);
Path path = new Path();
path.moveTo(startPoint.x,startPoint.y);//起点
path.lineTo(endPoint.x, endPoint.y);//终点
canvas.drawPath(path, paint);
}
}
}
训练技能点
Ø MapView
Ø MapController
Ø Overlay
Ø Projection
需求说明
在示例10.2中,我们在输入经纬度或地址之后会将相应的信息以文本的形式显示在Activity中,这样做对我们普通用户来说还是不大直观,如果能够将位置信息直接显示在地图上就好了,本实训的任务就是要求大家实现类似的功能。
巩固练习
一、选择题
1. Google Map提供了多少种视图()
A. 1种:矢量地图
B. 2种:矢量地图、卫星地图
C. 3种:矢量地图、卫星地图、地形视图
D. 4种:矢量地图、卫星地图、地形视图、三维地图
2. 下列说法正确的是()
A. 地理解析指的是将地址字符串转换为经纬度
B. 反向地理解析指的是将经纬度转换为地址字符串
C. 进行Google Map开发必须获取 Map API Key
二、上机练习
结合GPS与Google Map技术,模拟开发一个简单的GPS导航应用。当设备移动时,要求在地图上绘制出移动的轨迹(选做)。