功能描述:实现一个普通的地图展示,地图上有当前定位小蓝点和门店定位Marker信息。当前定位需要从定位服务异步获取,可能失败,门店的位置从接口异步获取,两个异步的数据最终展示在一个地图View上,并且Marker需要展示info window,需要两者不重叠不遮挡完全展示。
效果如下图:
接入准备:
1、下载jar包(如Android_Map3D_SDK_V5.2.1_20170630.jar),放入工程libs目录。
2、build.gradle文件添加:
compile 'com.amap.api:3dmap:latest.integration'
3、添加meta-date,appkey信息。
4、添加地图需要的权限,如定位等。
使用步骤:
xml中添加布局:
<com.amap.api.maps.MapView
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_marginTop="30dp" />
SDK底层使用OpenGL ES实现了两个地图组件,分别是GLSurfaceView、TextureView。
特点:
1、GLSurfaceView
包括 MapView、MapFragment、SupportMapFragment 三种容器。下面简单介绍一下 SupportMapFragment:
MapFragment 是 Android Fragment 类的一个子类,用于在 Android Fragment 中放置地图。 MapFragment 也是地图容器,与 MapView 一样提供对 AMap 对象(地图的控制类)的访问权。与 MapView 相比 SupportMapFragment 方便之处在于其可以更好的管理地图的生命周期,布局灵活。
2、TextureView
包括TextureMapView、TextureMapFragment、TextureSupportMapFragment 三种容器。
使用场景:您将MapView与其他的GLSurfaceView(比如相机)叠加展示,或者是在ScrollView中加载地图时,建议使用TextureMapView及SupportTextureMapFragment来展示地图,可以有效解决 GLSurfaceView 叠加时出现的穿透、滚动黑屏等问题。
根据需求合理选择实现的组件和方式
初始化MapView和管理对象AMap,显示地图
MapView mMapView.onCreate(savedInstanceState);
aMap = mMapView.getMap();
// info window的适配器
aMap.setInfoWindowAdapter(new AMap.InfoWindowAdapter() {
@Override
public View getInfoWindow(Marker marker) {
return null;
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
}
// 搞一些marker,然后给marker设置info window,这个过程由aMap来完成
Marker maker = aMap.addMarker(new MarkerOptions()
.icon(BitmapDescriptorFactory.fromBitmap(
BitmapFactory.decodeResource(getResources(), R.drawable.xxx)))
.title("title")
.snippet("xxx")
.anchor(0.5F, 0.5F)
.position(storeLatLng)
.draggable(false)
.setFlat(true));
// 设置地图的UI属性
UiSettings uiSettings = aMap.getUiSettings();
// 隐藏缩放控件
uiSettings.setZoomControlsEnabled(false);
// 隐藏比例尺控件
uiSettings.setScaleControlsEnabled(false);
...
// 设置事件交互
aMap.setOnMarkerClickListener(new AMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
// 消耗事件,不继续传递
return true;
}
});
aMap.setOnInfoWindowClickListener(new AMap.OnInfoWindowClickListener() {
@Override
public void onInfoWindowClick(Marker marker) {
// 点击打开导航dialog
ToastUtil.showShortMsg(XXX.this, "打开导航dialog");
}
});
// 开始定位,并显示定位小蓝点
final MyLocationStyle myLocationStyle = new MyLocationStyle();
// 设置自定义小蓝点的图标
myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.mylocation));
// 设置小蓝点的锚点
myLocationStyle.anchor(0.5F, 0.5F);
// 设置小蓝点的边框颜色
myLocationStyle.strokeColor(getResources().getColor(R.color.blue));
// 设置小蓝点的填充颜色
myLocationStyle.radiusFillColor(Color.argb(100, 29, 161, 242));
// 设置小蓝点的边框粗细
myLocationStyle.strokeWidth(1.0F);
// 表示地图只定位一次
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_SHOW);
// 是否显示定位小蓝点
myLocationStyle.showMyLocation(true);
aMap.setMyLocationStyle(myLocationStyle);
// 高德api在5.0对定位的实现做了更改,官网(https://lbs.amap.com/api/android-sdk/guide/create-map/mylocation)
aMap.setOnMyLocationChangeListener(new AMap.OnMyLocationChangeListener() {
@Override
public void onMyLocationChange(Location location) {
// 从location对象中获取经纬度信息,地址描述信息,建议拿到位置之后调用逆地理编码接口获取(获取地址描述数据章节有介绍)
if (location != null) {
locationLatLng = new LatLng(location.getLatitude(), location.getLongitude());
if (storeLatLng != null) {
zoomToSpanWithCenter();
}
}
}
});
// 设置为true表示启动显示定位小蓝点,false表示隐藏定位小蓝点并不进行定位,默认是false。
aMap.setMyLocationEnabled(true);
现在地图就可以正常展示了,定位完成后,会显示定位小蓝点,我们设置为只定位一次,这样可以节省性能和电量,并且也符合需求。
还有一些问题:
1、高德地图缩放级别分为大概20级,我们需要显示一个合适的级别。
2、我们给marker设置的info window会出现被遮挡的情况。
接下来对地图做进一步的约束
1、缩放到合适的级别:
LatLngBounds.Builder这个api可以帮助我们获取最佳的展示缩放比例,我们需要指定一个中心点,另外在传入一些我们需要展示的经纬度的集合,然后它会返给我们一个存储有合适范围约束的LatLngBounds对象。
private LatLngBounds getLatLngBounds(LatLng centerPoint, List<LatLng> pointList) {
LatLngBounds.Builder builder = LatLngBounds.builder();
if (centerPoint != null) {
for (int i = 0; i < pointList.size(); i++) {
LatLng p = pointList.get(i);
LatLng p1 = new LatLng((centerPoint.latitude * 2) - p.latitude,
(centerPoint.longitude * 2) - p.longitude);
builder.include(p);
builder.include(p1);
}
}
return builder.build();
}
有了合适范围约束对象后,我们的marker和当前位置小蓝点都可以展示在地图里,需要我们调整一下地图的显示
LatLngBounds bounds = getLatLngBounds(centerLatLng, pointList);
// 第二个参数为地图设置padding
aMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
设置好padding,防止显示的marker等离地图范围边挨得太近。
2、info window被遮挡,显示不全的问题
在一番搜索之后,发现并没有合适的api可以使用,┭┮﹏┭┮
突然想到是不是可以添加一些不显示的点来重新让地图定位,用这些不可见的点来把我们要显示的内容给圈起来,圈定一个范围。
相对于定位的小蓝点,门店的点有四个相对的位置,如下图:
其中,图2和图3容易被遮挡,我们在这两个位置的上方和右方添加两个点就可以显示我们的需求
public void zoomToSpanWithCenter() {
List<LatLng> pointList = new ArrayList<>();
LatLng centerLatLng;
LatLng helperLatLngVertical = null;
LatLng helperLatLngHorizontal = null;
// 判断店铺和当前位置哪个纬度更高,然后添加辅助点以更好地分配位置防止店铺info window显示不全
if (storeLatLng.latitude > locationLatLng.latitude) {
helperLatLngVertical = new LatLng(Math.max(storeLatLng.latitude, locationLatLng.latitude)
+ Math.abs(storeLatLng.latitude - locationLatLng.latitude), storeLatLng.longitude);
}
// 判断店铺和当前位置哪个经度更靠右侧,然后添加辅助点以更好地分配位置防止店铺info window显示不全
if (storeLatLng.longitude > locationLatLng.longitude) {
helperLatLngHorizontal = new LatLng(storeLatLng.latitude,
Math.max(storeLatLng.longitude, locationLatLng.longitude)
+ Math.abs(storeLatLng.longitude - locationLatLng.longitude) / 10);
}
pointList.add(storeLatLng);
pointList.add(locationLatLng);
if (helperLatLngVertical != null) {
pointList.add(helperLatLngVertical);
}
if (helperLatLngHorizontal != null) {
pointList.add(helperLatLngHorizontal);
}
centerLatLng = new LatLng((locationLatLng.latitude + storeLatLng.latitude
+ (helperLatLngVertical != null ? helperLatLngVertical.latitude : 0)
+ (helperLatLngHorizontal != null ? helperLatLngHorizontal.latitude:0)) / pointList.size(),
(locationLatLng.longitude + storeLatLng.longitude
+ (helperLatLngVertical != null ? helperLatLngVertical.longitude : 0)
+ (helperLatLngHorizontal != null ? helperLatLngHorizontal.longitude:0)) / pointList.size());
LatLngBounds bounds = getLatLngBounds(centerLatLng, pointList);
// 第二个参数为地图设置padding
aMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
}
最终显示不会被遮挡,并且缩放显示也合乎要求,O(∩_∩)O
扫尾工作:
1、检查有没有泄露情况、该释放的资源有没有及时释放
@Override
protected void onDestroy() {
super.onDestroy();
mMapViewStoreDetail.onDestroy();
if (mLocationClient != null) {
mLocationClient.onDestroy();
}
}
在Android Studio里打开Android Profiler查看打开、关闭activity情况下实例有没有及时回收释放。
2、有没有