技术开发 频道

使用Android Google Map开发LBS应用

  【IT168技术】在本系列的上一讲中,我们学习了google map 使用的一些简单步骤,其中学习到了如何注册google map api以及如何使用mapview控件。在本讲中,将学习如何根据若干指定的地理位置(给出了经纬度),并在地图上用指定的方法进行弹出显示其地理位置详细信息,其中用到了一个不错的第三方气球标识类库。

  步骤1 显示当前位置的信息

  在上一讲中,其实我们已经调用获得了用户的当前位置的信息,现在我们需要将其在页面的顶部显示出来,首先打开main.xml,然后添加如下的代码:

   <LinearLayout
    android:orientation
="horizontal"
    android:layout_width
="fill_parent"
    android:layout_height
="wrap_content"
    android:id
="@+id/infoLinearLayout"
    android:clickable
="true"
    android:onClick
="centerToCurrentLocation">
    
<TableLayout
        android:layout_width
="fill_parent"
        android:layout_height
="wrap_content"
        android:layout_marginTop
="30dp"
        android:background
="#97000000"
        android:padding
="7sp">
        
<TableRow>
            
<TextView
            android:layout_width
="fill_parent"
            android:layout_height
="wrap_content"
            android:layout_weight
="1"
            android:text
="@string/latitude"
            android:id
="@+id/latitudeText"
            android:textColor
="#FFFFFF">
            
</TextView>
            
<TextView
            android:layout_width
="fill_parent"
            android:layout_height
="wrap_content"
            android:layout_weight
="1"
            android:text
="@string/longitude"
            android:id
="@+id/longitudeText"
            android:textColor
="#FFFFFF">
            
</TextView>
        
</TableRow>
        
<TableRow>
            
<TextView
            android:layout_width
="fill_parent"
            android:layout_height
="wrap_content"
            android:layout_weight
="1"
            android:text
="@string/accuracy"
            android:id
="@+id/accuracyText"
            android:textColor
="#FFFFFF">
            
</TextView>
            
<TextView
            android:layout_width
="fill_parent"
            android:layout_height
="wrap_content"
            android:layout_weight
="1"
            android:text
="@string/provider"
            android:id
="@+id/providerText"
            android:textColor
="#FFFFFF">
            
</TextView>
        
</TableRow>
    
</TableLayout>
</LinearLayout>

  在上面的代码中,读者可能会注意到在LinearLayout中,使用了onclick事件,其中调用了centerToCurrentLocation这个方法,这其实为用户提供了方便,当用户在地图中导航时,如果要快速返回地图的中心点,只需要点一下地图的空白区域即可。

  在上面的代码中,我们还定义了很多文字的提示信息,这些要在strings.xml中进行定义,定义如下:

<string name="latitude">Latitude : </string>
<string name="longitude">Longitude : </string>
<string name="accuracy">Accuracy : </string>
<string name="provider">Provider : </string>

  现在,我们更新下程序,以在屏幕顶部显示当前位置的经纬度和地理位置提供者provider的信息,如下:

    ((TextView)findViewById(R.id.latitudeText)).setText("Latitude : " + String.valueOf((int)(currentLocation.getLatitude()*1E6)));
        ((TextView)findViewById(R.id.longitudeText)).setText(
"Longitude : " +  String.valueOf((int)(currentLocation.getLongitude()*1E6)));
        ((TextView)findViewById(R.id.accuracyText)).setText(
"Accuracy : " + String.valueOf(location.getAccuracy()) + " m");

  我们记得定义centerToCurrentLocation这个方法,该方法其实是调用了第一讲中的animateToCurrentLocation();方法,如下:

  public void centerToCurrentLocation(View view){

  animateToCurrentLocation();

  }

  步骤2 使用第三方的位置显示类库

  在这个步骤中,我们尝试做到通过在地图上标示若干固定位置的方法,吸引用户的注意。标注的方法我们希望通过图形去实现,其形状有点象大头针的样子,当用户点每个标示点时,会弹出显示该地点的详细信息。为了实现这个目的,我们找到了一款优秀的第三方位置显示类库,其下载地址在:https://github.com/jgilfelt/android-mapviewballoons/downloads

  如果用过Google Map地图的开发者可能会认为,Android自带的ItemizedOverlay类也可以实现类似的功能。没错,但ItemizedOverlay类没能象这个类库那样,当点击时默认自动象气泡那样弹出一个小的信息提示框,效果没这个COOL,所以我们选择类该类库。为了要使用该类库,我们要学习如何在已有的项目库中将这个类库工程包含进来。

  首先,将下载好的类库文件解压缩。然后在eclipse中,如下图,选择Import导入工程项目,

  点上图中的Next进入下一步,在select root directory中选择刚解压缩的文件所在目录,这时会出现如下图所示的两个工程项目,其中一个是库文件,另外一个是例子工程,我们同时都引入两个项目。

  点Finish键完成整个操作。然后会在工程资源管理器中看到有名为

  android-mapviewballoons的工程出现了,鼠标右键打开该工程,然后在下图工程项目的Android属性选项卡中,确保右下角的Is Library选项框被勾选

  现在,我们还需要在目前的工程中把这个引入的第三方工程包含进去。步骤为同样先打开现在的工程(MallFinder),然后打开到出现上图的样子,但这次选择的是“Add”按钮,添加一个工程,如下图,选择android-mapviewballoons工程为要包含的工程。

  最后点OK即可。

  步骤3 创建自定义的overlay类

  在这个步骤中,由于我们已经引入了第三方类库,因此在创建地图信息的自定义图层类时,可以继承这个第三方类库已经设计好的BalloonItemizedOverlay类,从而减轻很多的编码工作。现在进行相关操作:

步骤3 创建自定义的overlay类

  在工程的src目录下,找到com.shawnbe.mallfinder包,新建立一个类,如下图:

  由于要继承BalloonItemizedOverlay类,所以修改eclipse生成的代码为如下:

  public class MallOverlay extends BalloonItemizedOverlay

  我们同时再引入如下的一些类:

import android.graphics.drawable.Drawable;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.readystatesoftware.mapviewballoons.BalloonItemizedOverlay;

  完整的MallOverlay类代码如下:

package com.shawnbe.mallfinder;
import android.graphics.drawable.Drawable;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.readystatesoftware.mapviewballoons.BalloonItemizedOverlay;
  
public class MallOverlay extends BalloonItemizedOverlay<OverlayItem> {
  
    
public MallOverlay(Drawable defaultMarker, MapView mapView) {
        super(defaultMarker, mapView);
        
// TODO Auto-generated constructor stub
    }
  
    @Override
    protected OverlayItem createItem(
int i) {
        
// TODO Auto-generated method stub
        return
null;
    }
  
    @Override
    
public int size() {
        
// TODO Auto-generated method stub
        return
0;
    }
  
}

  上面是一个框架结构,现在要往其中增加一些实际的方法代码。首先,先声明一些全局变量,如下:

private Context mContext;
private ArrayList<OverlayItem> malls = new ArrayList<OverlayItem>();
private Location currentLocation;
然后修改如下代码:
    
public MallOverlay(Drawable defaultMarker, MapView mapView) {
    super(boundCenter(defaultMarker),mapView);
    boundCenter(defaultMarker);
    mContext
= mapView.getContext();
}
  
@Override
protected OverlayItem createItem(
int i) {
    
// TODO Auto-generated method stub
    return malls.get(i);
}
  
@Override
    
public int size() {
    
// TODO Auto-generated method stub
    return malls.size();
}
  
public void addOverlay(OverlayItem overlay) {
    malls.add(overlay);
    populate();
}
  
@Override
protected
boolean onBalloonTap(int index, OverlayItem item) {
    Toast.makeText(mContext,
"Overlay Item " + index + " tapped!",
            Toast.LENGTH_LONG).show();
    return
true;
}

  在上面的程序中,addOverlay方法用于向已有的地图层List中增加一个OverlayItem实例对象,而onBalloonTap方法中,则在当用户点某一个地图上的标注时弹出显示。

  步骤4 显示当前位置标识

  我们将用两种不同的图案来标识地图的标识,如下图,蓝色的图形表示当前用户所在的位置,红色的图形表示

步骤4 显示当前位置标识

  我们把这两张图下载下来,并且放到工程的drawable目录。这里将其放到res目录下的drawable-hdpi文件夹,因为本文只用到了HDPI的分辨率,在实际开发中,建议做中及低分辨率的图片已提供更好的适应性。

  接下来,编写一个方法,在当前位置中绘制出蓝色的标识。代码如下:

public void drawCurrPositionOverlay(){
    List
<Overlay> overlays = mapView.getOverlays();
    overlays.remove(currPos);
    Drawable marker
= getResources().getDrawable(R.drawable.me);
    currPos
= new MallOverlay(marker,mapView);
    
if(currentPoint!=null){
        OverlayItem overlayitem
= new OverlayItem(currentPoint, "Me", "Here I am!");
        currPos.addOverlay(overlayitem);
        overlays.add(currPos);
        currPos.setCurrentLocation(currentLocation);
    }
}

  同时要记得在主方法onCreate()方法中加入调用的语句:

  drawCurrPositionOverlay();

  运行应用,可以看到有如下图的结果:

步骤4 显示当前位置标识
 

  步骤5 增加各位置点的地图标识

  接下来,我们将在地图上增加若干个地点的经纬度(暂时只是固定编码格式,在实际应用中,应该是从数据库中读取),并用红色的图形标识出来。为了演示方便,在本文中,用硬编码的方式,强制设置用户当前的位置,因此修改setCurrentLocation方法如下:

public void setCurrentLocation(Location location){
    
/* int currLatitude = (int) (location.getLatitude()*1E6);
        
int currLongitude = (int) (location.getLongitude()*1E6);
        currentPoint
= new GeoPoint(currLatitude,currLongitude); */  
  
  
        currentPoint
= new GeoPoint(29647929,-82352486);
        currentLocation
= new Location("");
        currentLocation.setLatitude(currentPoint.getLatitudeE6()
/ 1e6);
        currentLocation.setLongitude(currentPoint.getLongitudeE6()
/ 1e6);
  
        ((TextView)findViewById(R.id.latitudeText)).setText(
"Latitude : " + String.valueOf((int)(currentLocation.getLatitude()*1E6)));
        ((TextView)findViewById(R.id.longitudeText)).setText(
"Longitude : " +  String.valueOf((int)(currentLocation.getLongitude()*1E6)));
        ((TextView)findViewById(R.id.accuracyText)).setText(
"Accuracy : " + String.valueOf(location.getAccuracy()) + " m");
        drawCurrPositionOverlay();
    }

  可以看到,这里我们强制用currentPoint指定了一个经纬度的位置。接下来,在drawMalls()方法中,我们固定假设有6个不同的地理位置点(比如不同的商场位置),然后分别添加到图层中去,如下代码:

public void drawMalls(){
    Drawable marker
= getResources().getDrawable(R.drawable.malls);
    MallOverlay mallsPos
= new MallOverlay(marker,mapView);
        GeoPoint[] mallCoords
= new GeoPoint[6];
  
        
//Load Some Random Coordinates in Miami, FL
        mallCoords[
0] = new GeoPoint(29656582,-82411151);//The Oaks Mall
        mallCoords[
1] = new GeoPoint(29649831,-82376347);//Creekside mall
        mallCoords[
2] = new GeoPoint(29674146,-8238905);//Millhopper Shopping Center
        mallCoords[
3] = new GeoPoint(29675078,-82322617);//Northside Shopping Center
        mallCoords[
4] = new GeoPoint(29677017,-82339761);//Gainesville Mall
        mallCoords[
5] = new GeoPoint(29663835,-82325599);//Gainesville Shopping Center      
  
        List
<Overlay> overlays = mapView.getOverlays();
    OverlayItem overlayItem
= new OverlayItem(mallCoords[0], "The Oaks Mall", "6419 W Newberry Rd, Gainesville, FL 32605");
    mallsPos.addOverlay(overlayItem);
    overlayItem
= new OverlayItem(mallCoords[1], "Creekside Mall", "3501 Southwest 2nd Avenue, Gainesville, FL");
    mallsPos.addOverlay(overlayItem);
    overlayItem
= new OverlayItem(mallCoords[2], "Millhopper Shopping Center", "NW 43rd St & NW 16th Blvd. Gainesville, FL");
    mallsPos.addOverlay(overlayItem);
    overlayItem
= new OverlayItem(mallCoords[3], "Northside Shopping Center", "Gainesville, FL");
    mallsPos.addOverlay(overlayItem);
    overlayItem
= new OverlayItem(mallCoords[4], "Gainesville Mall", "2624 Northwest 13th Street Gainesville, FL 32609-2834");
    mallsPos.addOverlay(overlayItem);
    overlayItem
= new OverlayItem(mallCoords[5], "Gainesville Shopping Center", "1344 N Main St Gainesville, Florida 32601");
    mallsPos.addOverlay(overlayItem);
    overlays.add(mallsPos);  
  
    mallsPos.setCurrentLocation(currentLocation);
}

  在上面的代码中,每生成一个新的地理位置点(overlayItem对象的实例),就将其添加到mallsPos中去(使用mallsPos.addOverlay(overlayItem)方法。最后记得还是要在onCreate方法中加上对drawMalls()方法的调用。

  运行程序后,可以看到如下的结果,其中有5个红色点标出了附近的5个商店的位置,蓝色点的位置表示当前用户的位置。

步骤5 增加各位置点的地图标识
 

  步骤6 显示距离各个标识点的距离信息

  现在,当我们点地图上的各个位置点时,会显示出该位置点的简要信息,但我们要设计得更深入点,希望当点这些位置点时,显示出这些点距离用户当前位置点有多远的距离,这样能给用户更直观良好的用户体验。下面我们开始动手进行改造。

  首先,打开MallOverlay.java文件,增加如下的方法。该方法的作用是接受GeoPoint类型作为参数并且返回的是Location类型的结果,因为在Android Google Map中,如果要使用distanceTo方法去计算距离,必须使用Location类型的变量。

public Location convertGpToLoc(GeoPoint gp){
    Location convertedLocation
= new Location("");
    convertedLocation.setLatitude(gp.getLatitudeE6()
/ 1e6);
    convertedLocation.setLongitude(gp.getLongitudeE6()
/ 1e6);
    return convertedLocation;
}

  接下来,我们在onBalloonTop事件中,修改代码如下,计算出用户当前点跟指定其他商店位置的距离。

protected boolean onBalloonTap(int index, OverlayItem item) {
    
String tmp = malls.get(index).getTitle();
    GeoPoint mallPoint
= malls.get(index).getPoint();
    Location tmpLoc
= convertGpToLoc(mallPoint);
    
double distance = ((currentLocation).distanceTo(tmpLoc))*(0.000621371192);
    DecimalFormat df
= new DecimalFormat("#.##");
    tmp
= tmp + " is " + String.valueOf(df.format(distance)) + " miles away.";
    Toast.makeText(mContext,tmp,Toast.LENGTH_LONG).show();
    return
true;
}

  上面的代码中,通过tmp变量获得了某个商店的名称,然后mallPoint是获得这个商店所在位置的GeoPoint是多少,然后通过convertGpToLoc方法转为Location对象的实例。然后调用distanceTo方法计算当前位置跟商店所在位置的距离,最后通过Toast的方式显示出来,如下图:

步骤6 显示距离各个标识点的距离信息

  到此,我们完成了一个基于Google Map的简单LBS的位置应用。本文的代码可以在如下地址下载:

  http://mobiletuts.s3.amazonaws.com/Android-SDK_Mall-Locations/2/MallFinderP2.zip

  小结

  在本系列的两篇教程中,我们学习了如何使用Google Map API的一些基本用法,包括如何注册使用Google Map API KEY,如何使用MapView控件,以及如何充分利用第三方的地理信息位置标识库,显示需要显示的地理位置信息。读者可以在本教程的基础上进行适当的扩展,以编写优秀的LBS手机应用。

0
相关文章