先来看效果图
1 本文实现的功能如下:
1.1 浏览本地文件,找到KML文件;
1.2 读取解析KML文件;
1.3 将KML文件中的位置信息显示在高德地图上;
总体框架和思路:打开文件对话框浏览*.kml文件,点击kml文件之后,返回此文件的路径,在AddSample.java类中获取到我们选择的kml文件的路径,然后调用ReadKml.java类中的parseKml方法,将KML文件路径传给parseKml以便解析。在解析KML时要先将KML文件解压缩(也可以不用解压缩),目的是解析KML压缩文件内的doc.kml。我们解析到KML文件中我们需要的属性值,x,y,name之后,要建立一个Coordinate类,用来存放上述的三个属性,每个Coordinate实例化对象才是我们所需要的。此处可以设置一个list,用来存放每个实例化对象。接下来便是参考高德地图的官方demo设置一下每个point的marker属性了。循环将其添加到地图上即可。
2 具体实现
2.1 打开文件对话框,浏览本地文件(此处完全用的是另外一大神博客中的代码,地址实在是找不到了,在此感谢)
2.1.1 先添加一个按钮的布局文件addsample.xml,点击此按钮后能够弹出打开文件的对话框
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/addsample_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add_sample" />
LinearLayout>
再添加一个浏览本地文件界面的布局文件filedialogitem.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/file_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFFFFF"
android:padding="4dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/filedialogitem_img"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="4dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/filedialogitem_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="#000000"
android:textStyle="bold"/>
<TextView
android:id="@+id/filedialogitem_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="#000000"/>
LinearLayout>
LinearLayout>
2.1.2 添加回调函数CallBackBundle.java
import android.os.Bundle;
public interface CallBackBundle {
void callBack(Bundle bundle);
}
2.1.3 添加OpenFileDialog.java,是实现浏览文件功能的主要代码
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.soil.soilsampling.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
public class OpenFileDialog {
public static String tag = "OpenFileDialog";
public static final String sRoot = "/"; //根目录
public static final String sParent = ".."; //父目录
public static final String sFolder = "."; //当前文件夹
public static final String sEmpty = "";
private static final String sErrorMsg = "访问出错!";
public static Dialog createDialog(int id, Context context, String title, CallBackBundle callBack, String suffix, Map images)
{
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setView(new FileSelectView(context, id, callBack, suffix, images));
Dialog dialog = builder.create();
dialog.setTitle(title);
return dialog;
}
// 点击打开浏览文件按钮后(或者加载样点按钮)出现的浏览磁盘文件的view
static class FileSelectView extends ListView implements OnItemClickListener
{
// 此view就是一个listview,每一行就是一个文件夹路径,每一行我们设置包括:文件夹图标,文件夹名称,路径
private CallBackBundle callBack = null;
private String path = sRoot;
private List
2.1.4 添加AddSample.java,是addsample.xml布局文件的实现
import java.util.HashMap;
import java.util.Map;
import com.soil.parsexml.ReadKml;
import com.soil.soilsampling.R;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
public class AddSample extends Activity{
static private int openFileDialogId = 0;
ReadKml readKml = new ReadKml();
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.addsample);
findViewById(R.id.addsample_button).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
showDialog(openFileDialogId);
}
});
}
@Override
protected Dialog onCreateDialog(int id) {
if (id == openFileDialogId) {
// 设置各种文件类型的图标
Map images = new HashMap();
images.put(OpenFileDialog.sRoot, R.drawable.filedialog_root);//根目录图标
images.put(OpenFileDialog.sParent, R.drawable.up32);//返回上一层,父目录图标
images.put(OpenFileDialog.sFolder, R.drawable.folder34);//文件夹图标
images.put("kml", R.drawable.kml32);
//images.put("kmz", R.drawable.kml32);
images.put(OpenFileDialog.sEmpty, R.drawable.filedialog_root);
Dialog dialog = OpenFileDialog.createDialog(id,this , "打开文件", new CallBackBundle() {
@Override
public void callBack(Bundle bundle) {
// TODO Auto-generated method stub
String filePath = bundle.getString("path");
//String fileName = bundle.getString("name");
setTitle(filePath);
try {
readKml.parseKml(filePath);//调用ReadKML类中的解析方法
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, ".kml;", images);
return dialog;
}
return null;
}
}
至此,我们已经实现了打开文件对话框,浏览KML文件的功能,效果图如下:
2.2 读取并解析KML文件
我们要实现的就是,找到sdcard中的KML文件之后,点击,程序便开始解析KML文件。我们要在地图上显示出KML中的点,必须将KML文件中Placemark节点下面的coordinates节点中的x,y坐标解析出来。又每个x,y坐标必须是一对,因此我们建立一个Coordinate类专门用来存放x,y坐标。在解析KML时主要使用了dom4j包。在这里可以参考我的另外一篇博客:http://blog.csdn.net/hnyzwtf/article/details/50202405
2.2.1 新建ReadKml.java实现解析KML的核心功能
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jsoup.Jsoup;
import org.jsoup.select.Elements;
import com.soil.model.Coordinate;
import com.soil.soilsampling.MainActivity;
import android.util.Log;
public class ReadKml {
public static boolean addSampleSuccess = false; //判断读取KML是否成功
private Coordinate coordinate = null; //存储从KML文件中读取出来的坐标值和name
private static List coordinateList = new ArrayList();//存储每次实例化的Coordinate对象,每个Coordinate都保存着不同的x,y,name
public void parseKml(String pathName) throws Exception
{
File file = new File(pathName);//pathName为KML文件的路径
try {
ZipFile zipFile = new ZipFile(file);
ZipInputStream zipInputStream = null;
InputStream inputStream = null;
ZipEntry entry = null;
zipInputStream = new ZipInputStream(new FileInputStream(file));
while ((entry = zipInputStream.getNextEntry()) != null) {
String zipEntryName = entry.getName();
if (zipEntryName.endsWith("kml") || zipEntryName.endsWith("kmz")) {
inputStream = zipFile.getInputStream(entry);
parseXmlWithDom4j(inputStream);
}else if (zipEntryName.endsWith("png")) {
}
}
zipInputStream.close();
inputStream.close();
} catch (ZipException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Boolean parseXmlWithDom4j(InputStream input) throws Exception
{
SAXReader reader = new SAXReader();
Document document = null;
try {
document = reader.read(input);
Element root = document.getRootElement();//获取doc.kml文件的根结点
listNodes(root);
addSampleSuccess = true;
//选择sd卡中的kml文件,解析成功后即调用MainActivity中的添加marker的方法向地图上添加样点marker
MainActivity mainActivity = new MainActivity();
mainActivity.addSampleMarker();//调用MainActivity中的方法
} catch (DocumentException e) {
// TODO: handle exception
e.printStackTrace();
}
return addSampleSuccess;
}
//遍历当前节点下的所有节点
public void listNodes(Element node){
String name = "";//Placemark节点中的name属性
String x = "";//坐标x
String y = "";//坐标y
double d_x = 0.0;//对x作string to double
double d_y = 0.0;
try {
if ("Placemark".equals(node.getName())) {//如果当前节点是Placemark就解析其子节点
List placemarkSons = node.elements();//得到Placemark节点所有的子节点
for (Element element : placemarkSons) { //遍历所有的子节点
if ("name".equals(element.getName())) {
name = element.getText();
}
}
Element pointSon;//Point节点的子节点
Iterator i = node.elementIterator("Point");//遍历Point节点的所有子节点
while (i.hasNext()) {
pointSon = (Element)i.next();
String nodeContent = "";
nodeContent = pointSon.elementText("coordinates");//得到coordinates节点的节点内容
String nodeContentSplit[] = null;
nodeContentSplit = nodeContent.split(",");
x = nodeContentSplit[1];
y = nodeContentSplit[0];
d_x = Double.valueOf(x.trim());
d_y = Double.valueOf(y.trim());
}
coordinate = new Coordinate(d_x, d_y , name);
coordinateList.add(coordinate);//将每一个实例化的对象存储在list中
}
} catch (Exception e) {
e.printStackTrace();
}
//同时迭代当前节点下面的所有子节点
//使用递归
Iterator iterator = node.elementIterator();
while(iterator.hasNext()){
Element e = iterator.next();
listNodes(e);
}
}
public List getCoordinateList()
{
return this.coordinateList;
}
}
2.2.2 Coordinate类
package com.soil.model;
public class Coordinate {
private double x;
private double y;
private String name;
public Coordinate(double x, double y, String name)
{
this.x = x;
this.y = y;
this.name = name;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.3 设置要添加到地图上的point的一些属性
2.3.1 新建SoilSampleUtil.java
package com.soil.utils;
import java.util.ArrayList;
import java.util.List;
import android.util.Log;
import com.amap.api.maps.AMap;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.soil.model.Coordinate;
import com.soil.parsexml.ReadKml;
import com.soil.soilsampling.R;
/*
* 设置要添加到地图上的样点Marker的一些属性
* */
public class SoilSampleUtil {
public static AMap aMapUtil;//添加marker到地图上需要使用AMap类的实例化对象
static ReadKml readKml = new ReadKml();
private static MarkerOptions markerOption;
private static ArrayList markers = new ArrayList();
private static List sampleList = readKml.getCoordinateList();
static double x = 0.0;
static double y = 0.0;
public static void addSampleMarkersData()
{
if (markers.size() == 0) {
//设置marker的图标为默认的天蓝色气泡
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE);
for (int i = 0; i < sampleList.size(); i++) {
x = sampleList.get(i).getX();//获取marker的坐标值
y = sampleList.get(i).getY();
markerOption = new MarkerOptions();
markerOption.setFlat(true);
markerOption.anchor(0.5f, 0.5f);
markerOption.icon(bitmapDescriptor);
markerOption.position(new LatLng(x, y));
//Log.d("SoilSampleUtil", String.valueOf(i)+"-->"+String.valueOf(x)+","+String.valueOf(y));
try {
if (aMapUtil != null) {
Marker marker = aMapUtil.addMarker(markerOption);
markers.add(marker);
}
else {
Log.d("SoilSampleUtil", "aMap is null !!!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
2.3.2 我们在MainActivity中添加一个方法,调用2.3.1中的addSampleMarkersData方法即可。
public void addSampleMarker()
{
if (ReadKml.addSampleSuccess) {
SoilSampleUtil.addSampleMarkersData();
}
else {
Log.d("MainActivity", "addSampleSuccess is false or aMap is null");
}
}
另外,我们必须在onPause方法中添加以下代码
@Override
protected void onPause() {
//当点击添加样点数据按钮时,MainActivity就会隐藏不可见,因此,在其声明周期“暂停”之前,必须将AMap的实例化对象传给SoilSampleUtil中的aMapUtil
//以避免AMap实例化对象为空
SoilSampleUtil.aMapUtil = aMap;
super.onPause();
mapView.onPause();
}
3 至此,我们已经实现了解析KML文件并在高德地图加载marker的功能
效果图如下:
源码在这里:http://download.csdn.net/detail/hnyzwtf/9385155