一、概述
地图开发是android开发中比较常见的一个模块,以前做项目都直接使用了百度地图提供的定位SDK。现在公司在物联网的项目有个需求,不需要手机定位,而是由其他设备定位,定时将坐标直接返回给后台,再由后台返回给app,在app中拿到坐标进行展示,并实现比较ok的展示效果。
二、百度地图相关配置
在百度开发者网站上下载相应的SDK导入,配置key值,添加相应权限。
三、展示地图
1、我们的需求是后台返回坐标点,这里我们模拟获得了后台返回的3个坐标点,实际开发中应通过网络请求来获取。
/** * 获取坐标点的方法 */
private void getPoints() {
//获取坐标点
latLngsList = new ArrayList<LatLng>();
//模拟解析得到的坐标点
final LatLng point = new LatLng(39.913175, 116.400244);
final LatLng point2 = new LatLng(39.913375, 116.400644);
final LatLng point3 = new LatLng(39.912175, 116.402270);
latLngsList.add(point);
latLngsList.add(point2);
latLngsList.add(point3);
}
2、根据坐标点绘制marker图标
/** * 根据坐标点绘制Marker */
private void showLineMarker() {
//构建marker图标
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon_marka);
for (int i = 0; i < latLngsList.size(); i++) {
//构建MarkerOption,用于在地图上添加Marker
option = new MarkerOptions().icon(bitmap).position(latLngsList.get(i));
//生长动画
option.animateType(MarkerOptions.MarkerAnimateType.grow);
//在地图上添加Marker,并显示
mBaiduMap.addOverlay(option);
//设置Marker覆盖物的ZIndex
option.zIndex(i);
}
}
3、我们想要地图按比例缩放到屏幕中心位置,使这3个坐标点能同时显示在屏幕上,这就需要我们来计算3个点的中心点坐标,最大横坐标和最小横坐标的距离,最大纵坐标和最小纵坐标的距离。
/** * 比较选出集合中最大经纬度 */
private void getMax() {
for (int i = 0; i < latLngsList.size(); i++) {
double latitude = latLngsList.get(i).latitude;
double longitude = latLngsList.get(i).longitude;
latitudeList.add(latitude);
longitudeList.add(longitude);
}
maxLatitude = Collections.max(latitudeList);
minLatitude = Collections.min(latitudeList);
maxLongitude = Collections.max(longitudeList);
minLongitude = Collections.min(longitudeList);
}
/** * 计算两个Marker之间的距离 */
private void calculateDistance() {
distance = GeoHasher.GetDistance(maxLatitude, maxLongitude, minLatitude, minLongitude);
}
其中GeoHasher为自定义通过经纬度计算距离的工具类,在下面贴出(copy滴)。
4、通过计算结果设置中心点坐标和缩放比例
/** *根据距离判断地图级别 */
private void getLevel() {
int zoom[] = {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 1000, 2000, 25000, 50000, 100000, 200000, 500000, 1000000, 2000000};
Log.i("info", "maxLatitude==" + maxLatitude + ";minLatitude==" + minLatitude + ";maxLongitude==" + maxLongitude + ";minLongitude==" + minLongitude);
Log.i("info", "distance==" + distance);
for (int i = 0; i < zoom.length; i++) {
int zoomNow = zoom[i];
if (zoomNow - distance * 1000 > 0) {
level = 18 - i + 6;
//设置地图显示级别为计算所得level
mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder().zoom(level).build()));
break;
}
}
}
/** * 计算中心点经纬度,将其设为启动时地图中心 */
private void setCenter() {
center = new LatLng((maxLatitude + minLatitude) / 2, (maxLongitude + minLongitude) / 2);
Log.i("info", "center==" + center);
MapStatusUpdate status1 = MapStatusUpdateFactory.newLatLng(center);
mBaiduMap.animateMapStatus(status1, 500);
}
5、给Marker设置点击事件,点击时弹出一个View,并以动画移动到屏幕中心。
/** * 设置Marker点击事件 */
private void setMarkerClick() {
mBaiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
final LatLng ll = marker.getPosition();
//动态生成一个view对象,用户在地图中显示InfoWindow
final InfoView infoView = new InfoView(getApplicationContext());
infoView.setBackgroundResource(R.drawable.button);
infoView.setTv1("1号车", 14, Color.GREEN);
infoView.setTv2("温度:20", 10, Color.RED);
infoView.setTv3("湿度:101", 10, Color.BLACK);
infoView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int j = 0; j < latLngsList.size(); j++) {
LatLng point = latLngsList.get(j);
if (ll.equals(point)) {
Toast.makeText(MainActivity.this, "point=" + point, Toast.LENGTH_SHORT).show();
}
}
}
});
//初始化infoWindow,最后那个参数表示显示的位置相对于覆盖物的竖直偏移量,这里也可以传入一个监听器
infoWindow = new InfoWindow(infoView, ll, -100);
mBaiduMap.showInfoWindow(infoWindow);//显示此infoWindow
//让地图以被点击的覆盖物为中心
MapStatusUpdate status = MapStatusUpdateFactory.newLatLng(ll);
// mBaiduMap.setMapStatus(status);
//以动画方式更新地图状态,动画耗时 500 ms
mBaiduMap.animateMapStatus(status, 500);
return true;
}
});
四、全部代码
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.pdwy.baidumap">
<!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<!-- 获取运营商信息,用于支持提供运营商信息相关的接口-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
<!-- 用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!-- 访问网络,网络定位需要上网-->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SD卡读取权限,用户写入离线定位数据-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
<meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="your key" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity
package com.pdwy.baidumap;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
import com.baidu.mapapi.map.InfoWindow;
import com.baidu.mapapi.map.LogoPosition;
import com.baidu.mapapi.map.MapStatus;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.Marker;
import com.baidu.mapapi.map.MarkerOptions;
import com.baidu.mapapi.model.LatLng;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private MapView mMapView;
private BaiduMap mBaiduMap;
private List<LatLng> latLngsList;
private MarkerOptions option;
private List<Double> latitudeList = new ArrayList<Double>();
private List<Double> longitudeList = new ArrayList<Double>();
private double maxLatitude;
private double minLatitude;
private double maxLongitude;
private double minLongitude;
private double distance;
private float level;
private LatLng center;
private InfoWindow infoWindow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在使用SDK各组件之前初始化context信息,传入ApplicationContext
//注意该方法要再setContentView方法之前实现
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_baidumap);
init(); //初始化相关配置
start(); //开始获取坐标点定位
}
@Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
mMapView.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView. onResume (),实现地图生命周期管理
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理
mMapView.onPause();
}
private void init() {
mMapView = (MapView) findViewById(R.id.mapView);
mBaiduMap = mMapView.getMap();
//普通地图
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
//设置logo位置,默认在左下角显示,不可以移除。使用枚举类型控制显示的位置,共支持6个显示位置(左下,中下,右下,左上,中上,右上)。
mMapView.setLogoPosition(LogoPosition.logoPostionleftBottom);
}
private void start() {
//获取坐标点(模拟,实际为后台返回坐标,需异步加载)
getPoints();
//根据坐标点绘制Marker
showLineMarker();
//比较选出集合中最大经纬度
getMax();
//计算两个Marker之间的距离
calculateDistance();
//根据距离判断地图级别
getLevel();
//计算中心点经纬度,将其设为启动时地图中心
setCenter();
//设置marker点击事件
setMarkerClick();
}
/** * 获取坐标点的方法 */
private void getPoints() {
//获取坐标点
latLngsList = new ArrayList<LatLng>();
//模拟解析得到的坐标点
final LatLng point = new LatLng(39.913175, 116.400244);
final LatLng point2 = new LatLng(39.913375, 116.400644);
final LatLng point3 = new LatLng(39.912175, 116.402270);
// maxLatitude==39.963175;minLatitude==39.913375;maxLongitude==116.40227;minLongitude==116.200244
latLngsList.add(point);
latLngsList.add(point2);
latLngsList.add(point3);
}
/** * 根据坐标点绘制Marker */
private void showLineMarker() {
//构建marker图标
BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon_marka);
for (int i = 0; i < latLngsList.size(); i++) {
//构建MarkerOption,用于在地图上添加Marker
option = new MarkerOptions().icon(bitmap).position(latLngsList.get(i));
//生长动画
option.animateType(MarkerOptions.MarkerAnimateType.grow);
//在地图上添加Marker,并显示
mBaiduMap.addOverlay(option);
//设置Marker覆盖物的ZIndex
option.zIndex(i);
}
}
/** * 比较选出集合中最大经纬度 */
private void getMax() {
for (int i = 0; i < latLngsList.size(); i++) {
double latitude = latLngsList.get(i).latitude;
double longitude = latLngsList.get(i).longitude;
latitudeList.add(latitude);
longitudeList.add(longitude);
}
maxLatitude = Collections.max(latitudeList);
minLatitude = Collections.min(latitudeList);
maxLongitude = Collections.max(longitudeList);
minLongitude = Collections.min(longitudeList);
}
/** * 计算两个Marker之间的距离 */
private void calculateDistance() {
distance = GeoHasher.GetDistance(maxLatitude, maxLongitude, minLatitude, minLongitude);
}
/** *根据距离判断地图级别 */
private void getLevel() {
int zoom[] = {10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 1000, 2000, 25000, 50000, 100000, 200000, 500000, 1000000, 2000000};
Log.i("info", "maxLatitude==" + maxLatitude + ";minLatitude==" + minLatitude + ";maxLongitude==" + maxLongitude + ";minLongitude==" + minLongitude);
Log.i("info", "distance==" + distance);
for (int i = 0; i < zoom.length; i++) {
int zoomNow = zoom[i];
if (zoomNow - distance * 1000 > 0) {
level = 18 - i + 6;
//设置地图显示级别为计算所得level
mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder().zoom(level).build()));
break;
}
}
}
/** * 计算中心点经纬度,将其设为启动时地图中心 */
private void setCenter() {
center = new LatLng((maxLatitude + minLatitude) / 2, (maxLongitude + minLongitude) / 2);
Log.i("info", "center==" + center);
MapStatusUpdate status1 = MapStatusUpdateFactory.newLatLng(center);
mBaiduMap.animateMapStatus(status1, 500);
}
/** * 设置Marker点击事件 */
private void setMarkerClick() {
mBaiduMap.setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
final LatLng ll = marker.getPosition();
//动态生成一个view对象,用户在地图中显示InfoWindow
final InfoView infoView = new InfoView(getApplicationContext());
infoView.setBackgroundResource(R.drawable.button);
infoView.setTv1("1号车", 14, Color.GREEN);
infoView.setTv2("温度:20", 10, Color.RED);
infoView.setTv3("湿度:101", 10, Color.BLACK);
infoView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int j = 0; j < latLngsList.size(); j++) {
LatLng point = latLngsList.get(j);
if (ll.equals(point)) {
Toast.makeText(MainActivity.this, "point=" + point, Toast.LENGTH_SHORT).show();
}
}
}
});
//初始化infoWindow,最后那个参数表示显示的位置相对于覆盖物的竖直偏移量,这里也可以传入一个监听器
infoWindow = new InfoWindow(infoView, ll, -100);
mBaiduMap.showInfoWindow(infoWindow);//显示此infoWindow
//让地图以被点击的覆盖物为中心
MapStatusUpdate status = MapStatusUpdateFactory.newLatLng(ll);
// mBaiduMap.setMapStatus(status);
//以动画方式更新地图状态,动画耗时 500 ms
mBaiduMap.animateMapStatus(status, 500);
return true;
}
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<com.baidu.mapapi.map.MapView android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height="match_parent"></com.baidu.mapapi.map.MapView>
</LinearLayout>
通过经纬度计算距离的工具类GeoHasher
package com.pdwy.baidumap;
import java.util.HashMap;
import java.util.Map;
public class GeoHasher {
private static String BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
private static double EARTH_RADIUS = 6378.137;
private static double rad(double d) {
return d * Math.PI / 180.0;
}
public static String convertToString(char[] a) {
String s = "";
for (int i = 0; i < a.length; i++) {
s += a[i];
}
return s;
}
public static double GetDistance(double lat1, double lng1, double lat2,
double lng2) {
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
+ Math.cos(radLat1) * Math.cos(radLat2)
* Math.pow(Math.sin(b / 2), 2)));
s = s * EARTH_RADIUS;
return s;
}
public String encode_geohash(double latitude, double longitude,
int precision) {
char[] geohash = new char[precision + 1];
boolean is_even = true;
int i = 0;
double[] lat = new double[2];
double[] lon = new double[2];
double mid;
char bits[] = { 16, 8, 4, 2, 1 };
int bit = 0, ch = 0;
lat[0] = -90.0;
lat[1] = 90.0;
lon[0] = -180.0;
lon[1] = 180.0;
while (i < precision) {
if (is_even) {
mid = (lon[0] + lon[1]) / 2;
if (longitude > mid) {
ch |= bits[bit];
lon[0] = mid;
} else
lon[1] = mid;
} else {
mid = (lat[0] + lat[1]) / 2;
if (latitude > mid) {
ch |= bits[bit];
lat[0] = mid;
} else
lat[1] = mid;
}
is_even = !is_even;
if (bit < 4)
bit++;
else {
geohash[i++] = BASE32.charAt(ch);
bit = 0;
ch = 0;
}
}
geohash[i] = 0;
String s = "";
for (i = 0; i < geohash.length; i++)
s += geohash[i];
return s;
}
public String[] expand(String geoStr) {
String eastNeighbour = getEastNeighbour(geoStr);
String westNeighbour = getWestNeighbour(geoStr);
String northNeighbour = getNorthNeibour(geoStr);
String southNeighbour = getSouthNeibour(geoStr);
String[] expandGeoStr = { geoStr, eastNeighbour, westNeighbour,
northNeighbour, southNeighbour, getNorthNeibour(westNeighbour),
getNorthNeibour(eastNeighbour), getSouthNeibour(westNeighbour),
getSouthNeibour(eastNeighbour) };
return expandGeoStr;
}
public String getEastNeighbour(String geoStr) {
Map<String, Object> map = extractLonLatFromGeoStr(geoStr);
long lon = (Long) map.get("lon") + 1;
return getGeoStrFrom(lon, (String) map.get("latBitStr"), true);
}
public String getWestNeighbour(String geoStr) {
Map<String, Object> map = extractLonLatFromGeoStr(geoStr);
long lon = (Long) map.get("lon") - 1;
return getGeoStrFrom(lon, (String) map.get("latBitStr"), true);
}
public String getNorthNeibour(String geoStr) {
Map<String, Object> map = extractLonLatFromGeoStr(geoStr);
long lat = (Long) map.get("lat") + 1;
return getGeoStrFrom(lat, (String) map.get("lonBitStr"), false);
}
public String getSouthNeibour(String geoStr) {
Map<String, Object> map = extractLonLatFromGeoStr(geoStr);
long lat = (Long) map.get("lat") - 1;
return getGeoStrFrom(lat, (String) map.get("lonBitStr"), false);
}
public Map<String, Object> extractLonLatFromGeoStr(String geoStr) {
boolean is_even = true;
char bits[] = { 16, 8, 4, 2, 1 };
int bit = 0, ch = 0;
int geoIdx;
String lonBitStr = "";
String latBitStr = "";
long lon = 0;
long lat = 0;
for (int i = 0; i < geoStr.length(); i++) {
geoIdx = BASE32.indexOf(geoStr.charAt(i));
for (bit = 0; bit < 5; bit++) {
ch = geoIdx & bits[bit];
if (is_even) {
if (ch != 0) {
lonBitStr += "1";
lon = lon * 2 + 1;
} else {
lonBitStr += "0";
lon = lon * 2;
}
} else {
if (ch != 0) {
latBitStr += "1";
lat = lat * 2 + 1;
} else {
latBitStr += "0";
lat = lat * 2;
}
}
is_even = !is_even;
}
}
Map<String, Object> map = new HashMap<String, Object>();
map.put("lonBitStr", lonBitStr);
map.put("latBitStr", latBitStr);
map.put("lat", lat);
map.put("lon", lon);
return map;
}
public String getGeoStrFrom(long lonOrLat, String lonOrLatStr, boolean isLon) {
String lonBitStr = "";
String latBitStr = "";
if (isLon) {
lonBitStr = Long.toBinaryString(lonOrLat);
latBitStr = lonOrLatStr;
} else {
latBitStr = Long.toBinaryString(lonOrLat);
lonBitStr = lonOrLatStr;
}
boolean is_even = true;
String geoStr = "";
int ch, bit;
int geoStrLength = (lonBitStr.length() + latBitStr.length()) / 5;
for (int i = 0; i < lonBitStr.length();) {
ch = 0;
for (bit = 0; bit < 5; bit++) {
if (is_even)
ch = ch * 2 + lonBitStr.charAt(i) - '0';
else {
if (i < latBitStr.length())
ch = ch * 2 + latBitStr.charAt(i) - '0';
else
bit--;
i++;
}
is_even = !is_even;
}
geoStr += BASE32.charAt(ch);
if (geoStr.length() == geoStrLength)
return geoStr;
}
return geoStr;
}
}
点击marker弹出的自定义view InfoView
package com.pdwy.baidumap;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
/** * @说明:点击marker弹出的自定义view * @作者:zry * @时间:2016/8/26 */
public class InfoView extends LinearLayout {
private TextView tv1,tv2,tv3;
public InfoView(Context context) {
this(context,null);
}
public InfoView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public InfoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context){
View.inflate(context, R.layout.view_info,this);
tv1= (TextView) findViewById(R.id.textView);
tv2= (TextView) findViewById(R.id.textView2);
tv3= (TextView) findViewById(R.id.textView3);
}
public void setTv1(String text, float size, int color){
tv1.setText(text);
tv1.setTextSize(size);
tv1.setTextColor(color);
}
public void setTv2(String text, float size, int color){
tv2.setText(text);
tv2.setTextSize(size);
tv2.setTextColor(color);
}
public void setTv3(String text, float size, int color){
tv3.setText(text);
tv3.setTextSize(size);
tv3.setTextColor(color);
}
}
view_info
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="11111" />
<TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="22222" />
<TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="33333" />
</LinearLayout>
button
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<corners android:radius="8dp"/>
<solid android:color="@android:color/white" android:dashGap="0dp" android:width="1dp"/>
</shape>
</item>
<item>
<shape>
<corners android:radius="8dp"/>
<solid android:color="@android:color/white" android:dashGap="0dp" android:width="1dp"/>
</shape>
</item>
</selector>