步骤一:

GoogleMap环境准备。

Android系统默认并不支持调用Google Map,为正常调用Google Map服务,需要先进行如下准备工作。

1)获取Map API Key

单击Eclipse主菜单:Window —>Preferences —> 单击左侧“Android”—> Build —> 弹出如图1所示。

151221371.jpg

图1查看Android模拟器的Keystore

2)上图中显示的便是模拟器的keystore的存储位置,接下来应用程序需要根据该keystore来生成Google API的Key。

3)用JDK提供的keytool工具为Android keystore生成认证指纹,启动命令行窗口输入如下命令:

keytool –list –keystore <Android keystore的存储位置>

其中<Android keystore的存储位置>要替换成图1中Androidkeystore的存储位置,如:Keytool -list -keystore "D:\Program Files\android-sdks \.android\debug.keystore"。

151229821.jpg

图2指纹

注:默认**库口令是:android

就会得到MD5的指纹,记录下此记录:二十段用冒号割开的数字段,每段是两个十六进制的数(图2中红色删除线下的部分)

4)在Google APIs Console上创建项目,并且注册Maps API。

首先,去这个网址:https://code.google.com/apis/console/

注:本文对申请API Key V2的说明是基于google developer console(上面的网址)网站旧版本的说明,现在已更新了,如果要返回旧版本可在打开网址后,选择旧版本。

170530461.jpg

用Gmail的账户登录,如果是第一次的话,需要创建项目,默认情况会创建一个叫做APIProject的项目。

登陆之后出现页面(如图3所示):

151243437.jpg

图3创建APIProject工程

单击“Createproject...”后到达页面(如图4所示):

151250852.jpg

图4 服务页面

点击左边的Services,会在中间看到很多的APIs和Services,找到GoogleMaps Android API v2,然后把它设置成on,需要接受一些服务条款,如图5所示。

151253977.jpg

图5 开启的服务页面

之后跳转到页面,如图6所示:

151255544.jpg

图6 同意条款

勾选同意条款,单击接受按钮。

5)获得API Key

在左边的导航条中选择API Access(如图7)。


151257162.jpg

图7 导航栏

在出来的页面中选择Create New Android Key...就可以生成key了(如图8所示)

151305346.jpg

图8 生成Key

然后在对话框中填入:SHA-1 指纹,分号隔开,然后是应用的 package name.然后就会生成一个Key,如图9所示。151308140.jpg9 生成自己的Key操作图

最后生成的API Key如图10所示:

151310143.jpg

10 生成的API Key

步骤二:

把API Key加入应用程序。

1)首先,新建Android应用程序。创建应用程序时,注意包名应该和申请key时候的包名一致。

   2)之后修改AndroidManifest.xml文件,在<application>元素中加入子标签

<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="your_api_key" />

注:其中your_api_key置换成自己申请的API Key。

在该文件中加入一些许可信息,允许必要的权限。

<permission
    android:name="com.example.mapdemo.permission.MAPS_RECEIVE"
    android:protectionLevel="signature"/>
<uses-permission
    android:name="com.example.mapdemo.permission.MAPS_RECEIVE"/>

注:其中com.example.mapdemo换成自己的包名。

步骤三:


AndroidManifest.xml文件中的其他的选项设置。

1)许可设置

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="com.google.android.providers.gsf.permission.READ_GSERVICE S"/>
<uses-permission
    android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission
    android:name="android.permission.ACCESS_FINE_LOCATION"/>

2)OpenGL ES V2特性支持(作为<manifest> 的子元素)

<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>

步骤


在布局文件中加上地图。

<fragment xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"/>

遇到的问题和解决的方法。

程序编译错误,显示找不到一些类,如图11所示。

151312459.jpg

11 错误提示信息


解决这个问题,首先需要把Google Play services的类库加载进来:

   在Eclipse里面选择:File > Import >Android > Existing Android Code Into Workspace然后点击Next。

之后Browse..., 找到路径下的<android-sdk-folder>/extras/google/google_play_services/ libproject/google-play-services_lib,然后选择Finish。

   添加对这个库的引用在自己的项目上右键,选Properties,左边选Android,然后在下面的Library里面Add刚才的google-play-services_lib,如图12所示。

151316157.jpg

12 引用类库步骤图

之后运行程序就应该出来地图了。运行过程中,有可能会碰到下面的问题:程序运行成功,但是显示This app won't run unlessyou update Google Play services,如图13所示。需要点击Update,按照提示操作。

151318656.jpg

13 提示界面

   之后运行程序,就出地图了,如14所示。151321922.jpg

图14 运行结果图

注:

因为MapFragment只在API 12及之后的版本才有,所以对于之前的版本需要使用Support Library来进行辅助。

如果minSdkVersion设置为12以前的,就需要使用Support Library

需要更改的地方是:布局文件中,把MapFragment改为SupportMapFragment。

   MainActivity继承自FragmentActivity而不是Activity。(需要import android.support.v4. app.FragmentActivity;)。

步骤


新建工程GoogleMapTest,包名为com.example.googlemaptest,将一张名为pos.png的图片导入到drawable目录下,用于标注当前的位置。项目中Activity的名字为MainActivity.java,对应的布局文件名字为activity_main.xml,布局文件中包括2个输入框,用于接收用户输入的经纬度信息,有1个按钮,用于根据用户输入的经纬度定位具体位置;有两个单选按钮,分别显示普通地图和卫星地图;有1个地图组件。该工程目录结构及布局文件界面如图15所示:

151215166.jpg

15 工程目录结构图

步骤


   编辑地图组件对应的布局文件googlemap.xml,主要代码如下。

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"
map:cameraBearing="45"
map:cameraTargetLat="25.033611"
map:cameraTargetLng="121.565000"
map:cameraTilt="0"
map:cameraZoom="13"
map:uiCompass="true"
map:mapType="normal"
map:uiRotateGestures="true"
map:uiScrollGestures="true"
map:uiTiltGestures="true"
map:uiZoomControls="false"
map:uiZoomGestures="true" />

注:此文件中,class属性以下的几行代码,是用于设置地图的一些属性,这部分内容不是必须的,可以根据自己的需要进行设置,也可以都不设置,而在Activity中使用代码进行设置。

当在XML文件中加入这些map属性的设置时,必须要添加命名空间:xmlns:map="http://schemas.android.com/apk/res-auto",并且该XML文件中不能再加入其它的组件,例如编辑框,甚至不能为其添加容器,例如LinearLayout。如果设置了这些属性,并且还想添加其它组件,就需要将该XML文件作为主XML布局文件的一部分,通过<include>标签包含在主布局文件中。具体方法稍后具体说明。

   如果不在XML文件中设置这些map属性的话,该XML文件可以不添加命名空间,并且可以在此文件中添加其它的组件。

步骤

编写主布局文件:activity_main.xml,采用线性布局并将上面的地图组件对应的XML文件(googlemap.xml)包含进来。

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/textView1"
    android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="定位"
android:textSize="15sp" />
<LinearLayout
    android:id="@+id/location"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >
<!-- 定义输入经度值的文本框 -->
<EditText
        android:id="@+id/edt_lng"
    android:layout_width="wrap_content"
android:layout_height="wrap_content"
        android:ems="6"
            android:hint="经度"
android:textSize="20sp" />
<!-- 定义输入纬度值的文本框 -->
<EditText
android:id="@+id/edt_lat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:ems="6"
android:hint="纬度"
android:textSize="20sp" />
<Button
android:id="@+id/btn_loc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="定位"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/mapsChoice"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<!-- 定义选择地图类型的单选按钮 -->
<RadioGroup
    android:id="@+id/rg_mapType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb_nomal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="普通视图" />
 <RadioButton
android:id="@+id/rb_satellite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="卫星视图" />
</RadioGroup>
</LinearLayout>
</LinearLayout>
<include android:id="@+id/googleMap"
layout="@layout/googlemap" />

步骤

编写Activity文件,MainActivity.java

声明各组件及必要的类。

//定义界面上的可视化组件
private Button btn_loc;
private EditText edt_lng, edt_lat;
private RadioGroup rg_mapType;
GoogleMap mMap;
private CameraPosition cameraPosition;
private MarkerOptions markerOpt;
//定义LocationManager对象
private LocationManager locManager;
private Location location;
private String bestProvider;

   在onCreate()方法中获取各组件,并给按钮注册事件监听器,实现地理位置的实时定位。

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取用户界面的组件
findViews();
//创建LocationManager对象,并获取Provider
initProvider();
//取得地图组件
mMap = ((MapFragment)getFragmentManager()
.findFragmentById(R.id.mapView)).getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
//更新位置信息
updateToNewLocation(location);
//给按钮添加监听器
btn_loc.setOnClickListener(new MapClickedListener());
//为RadioGroup的选中状态改变添加监听器
rg_mapType.setOnCheckedChangeListener(new ChangeMapTypeListener());
// 设置监听器,自动更新的最小时间为间隔N秒(1秒为1*1000,这样写主要为了方便)或最小位移变化超过N
locManager.requestLocationUpdates(bestProvider,  3 * 1000, 8
, new LocationListener() {       
//当Provider的状态改变时
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
// 当GPS LocationProvider可用时,更新位置
location = locManager.getLastKnownLocation(provider);
}
@Override
public void onProviderDisabled(String provider) {
updateToNewLocation(null);
}
@Override
public void onLocationChanged(Location location) {
// 当GPS定位信息发生改变时,更新位置
        updateToNewLocation(location);
}
});
}
private void initProvider() {
//创建LocationManager对象
locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// List all providers:
List<String> providers = locManager.getAllProviders();
Criteria criteria = new Criteria();
bestProvider = locManager.getBestProvider(criteria, false);
location = locManager.getLastKnownLocation(bestProvider);
System.out.println("经度:"+location.getLatitude()+"纬度:" + location.getLongitude());
}
//获取用户界面组件
private void findViews() {
//获取界面上的两个按钮
btn_loc = (Button) findViewById(R.id.btn_loc);
//获取界面上的两个文本框
edt_lng = (EditText) findViewById(R.id.edt_lng);
edt_lat = (EditText) findViewById(R.id.edt_lat);
//获得RadioGroup
rg_mapType = (RadioGroup) findViewById(R.id.rg_mapType);
}

实现各个事件监听器类。

//定位按钮的点击事件监听器
private class MapClickedListener implements OnClickListener{
//根据用户输入经纬度定位
@Override
public void onClick(View v) {            
        //获取用户输入的经纬度
        String lng = edt_lng.getText().toString().trim();
        String lat = edt_lat.getEditableText().toString().trim();
        if(lng.equals("") || lat.equals("")){
        Toast.makeText(getApplicationContext(), "请输入有效的经纬度信息!
", Toast.LENGTH_LONG).show();
            location = locManager.getLastKnownLocation(bestProvider);
            updateToNewLocation(location);
}else{
        location.setLongitude(Double.parseDouble(lng));
        location.setLatitude(Double.parseDouble(lat));
        //调用方法更新地图定位信息
        updateToNewLocation(location);
}
}
}
private class ChangeMapTypeListener implements OnCheckedChangeListener{
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch(checkedId){       
        case R.id.rb_nomal://如果勾选的是"正常视图"的单选按钮
                mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                break;
            case R.id.rb_satellite://如果勾选的是"卫星视图"的单选按钮
                mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
                break;
        }
}
}

实现更新位置信息的方法。

private void updateToNewLocation(Location location){
mMap.clear();
//Marker1
markerOpt = new MarkerOptions();
double dLong = 114.51500;
double dLat = 38.042000;
if(location != null){
    //获取经度
    dLong = location.getLongitude();
    //获取纬度
    dLat = location.getLatitude();
}
markerOpt.position(new LatLng(dLat, dLong));
    markerOpt.draggable(false);
    markerOpt.visible(true);
    markerOpt.anchor(0.5f, 0.5f);//设为图片中心
    markerOpt.icon(BitmapDescriptorFactory.fromResource(R.drawable.pos));
    mMap.addMarker(markerOpt);
    //将摄影机移动到指定的地理位置
    cameraPosition = new CameraPosition.Builder()
            .target(new LatLng(dLat, dLong))
            .zoom(15) // 缩放比例
            .bearing(0) // Sets the orientation of the camera to east
            .tilt(30)  // Sets the tilt of the camera to 30 degrees
            .build();// Creates a CameraPosition from the builder
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
}

步骤

编辑AndroidManifest.xml,需要添加必要的权限及所申请的API Key

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   …  >
    <uses-sdk
       … />
    <!—下面的权限中,com.example.googlemaptest 要换成你的工程文件的包名-->
    <permission android:name="com.example.googlemaptest.permission.MAPS_RECEIVE"
        android:protectionLevel="signature"/>
<uses-permission
android:name="com.example.googlemaptest.permission.MAPS_RECEIVE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission
android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<!—下面的两条权限在API V2中是不要求的,但是在开发过程中建议添加 -->
    <uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>
    <application
        … >
        <activity
            android:name="com.example.googlemaptest.MainActivity"
…
        </activity>
        <meta-data android:name="com.google.android.maps.v2.API_KEY"
<!—下面的属性值要求添加你申请的API Key值-->
            android:value="你申请的API Key值 " />
        <uses-library android:required="true"
                android:name="com.google.android.maps" />
    </application>
</manifest>

步骤

运行改项目,测试定位功能。

   注:由于本项目提取历史定位数据,所以首次使用,在没有历史数据的情况下会闪退,这是因为第一次获取GPS的时候没有历史数据,location = locManager.getLastKnownLocation(provider)这条语句获取不到历史数据造成的,常见的解决办法有如下几种:

   1. 循环获取定位数据,直到获取到经纬度为止。该方法的缺点是获取不到数据时会进入死循环。

while(location  == null){
    lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 30000, 1, new LocListener()); 
}

   2. 优先采用NETWORK_PROVIDER,该Provider比GPS_PROVIDER的稳定性好。

   3. 读取定位数据之前,先让程序获取定位数据,给程序预留一定的时间,因为定位数据不一定一次性获取成功,这和位置、手机软硬件有关。

   4. 偷懒的方法是使用其他的软件如百度地图,首先获取到定位数据,有历史数据了以后再运行该程序。

   5. 在程序中给定初始化的经纬度数据。


附录:

新版本中对于已申请的API Key可以服务于多个包,直接添加就可以了。

新版本的谷歌开发网左侧的列表中,点击APIs & auth下的APIs,会显示所开启的服务。

左侧的列表如下图所示

173100165.jpg

点击Credentials列表可申请新的API Key,如下图示。

173322787.jpg

点击“CREATE NEW KEY”可申请新的Key,点击“Edit allowed Android applications”可编辑已有的API Key,也可使用已有Key,支持更多的服务,如下图所示。

173611287.jpg

   直接在框中添加Key所支持的包名即可,需要注意的是,Key与包名之间要命“;”隔开。之后点击“Update”按钮。


到此结束,希望对大家有帮助。

以上纯属个人观点,供新手交流学习。欢迎高手及大神批评指正。