开发基于 Google Map 的 Android 应用

转载: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" /> 

 

添加的相应的访问权限:

  • android.permission.ACCESS_COARSE_LOCATION: 允许应用通过网络信息获取当前地理位置信息的权限。
  • android.permission.ACCESS_FINE_LOCATION: 允许应用通过 GPS 获取当前地理位置信息的权限,较网络获得的地理位置信息更为精确。
  • android.permission.INTERNET: 允许应用访问网络的权限。

最终的 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 Map 的 Android 应用_第1张图片

 

加缩放控件并控制缩放比例,切换地图显示类型(卫星,地图,街道)

 

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. 地图的缩放控件

开发基于 Google Map 的 Android 应用_第2张图片

 

 

通过 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 系统中,提供获取当前地理位置信息的功能。

  • LocationManager: 提供访问 Android 地理信息服务的接口。
  • LocationProvider: 提供多种定位方式供开发者选择(GPS, NETWORK 定位等)。
  • Critieria:设置一系列的查询条件,来获取相应的 Location Provider。

下面通过一个具体的示例来说明,如何获取 Android 系统中最后一次更新的地理位置信息。

  1. 根据应用需求创建 Criteria 对象,设定获取地理位置信息的精度,方向信息等。
  2. 获取当前符合步骤 1 定义的规则最优的 LocationProvider。
  3. 获取 LocationManager 对象来访问 android 系统地理位置信息服务。
  4. 通过 LocationManager 对象提供的方法获取系统最后一次更新的地理位置信息。

示例代码如下:


清单 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,在这里需要提示用户当前地理位置信息服务未开启,否则将会有"java.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. 当前位置图标

 

  1. 创建 MyOverlay 类,继承自 ItemizedOverlay 抽象类,在 MyOverlay 对象中包括所有要在地图上标注的对象列表 mItems。
  2. 重写 createItem 方法,将需要在地图上标注的点转化为 OverlayItem 对象,OverlayItem 对象对应于在地图上标注的每一个点,其包括经纬度信息、名称、标题、图标等属性,这些属性均可以通过其对象中提供的设置方法进行修改。
  3. 调用 addMarker()方法,将获取的当前位置信息以图标方式显示在地图上。
  4. myMapOverlay类中重写 OnTap 方法,该方法会在图标点击事件触发时回调,传入参数为响应触击事件 overlayItem 对象在mItems中的位置。在我们的示例中,当图标点击事件触发时通过toast来显示当前位置信息
    清单 12. 在地图上添加图标
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

你可能感兴趣的:(开发基于 Google Map 的 Android 应用)