跳过导入高德地图,导入包,直接到使用
一、在Activity中使用
1、在布局中使用mapview
<com.amap.api.maps.MapView
android:id="@+id/amapView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#fff"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/estimate_payment" />
2、在Actvity中初始化地图
遇到过的问题:
(1)没有在onCreate中调用MapView.onCreate,导致地图是空白
(2)onPause里使用了MapView.OnPause,onResume里没有使用MapView.OnResume,导致mapview卡住一动不动(移动地图,缩放手势都不起作用)
(3)隐藏精度圈、缩放按钮、自定义定位蓝点(https://lbs.amap.com/api/android-sdk/guide/interaction-with-map/control-interaction) (4)自定义定位button:因为高德地图没有提供替换定位button的方法,所以需要将原本地图的定位button隐藏,添加自定义的button,点击之后执行定位。(http://lbsbbs.amap.com/forum.php?mod=viewthread&tid=14167)
(5)弹出popupwindow时设置背景变暗,mapView上面重叠部分的view被穿透。低版本上mapView整个就没有变暗。这个问题只要将mapView换成TextureMapView就可以了。官网上有个解释mapView与TextureMapView区别的:mapView是GLSurfaceView,与其他GLSurfaceView叠加会出现穿透现象,不知道我这边的问题是不是同一个原因。虽然TextureMapView效率比MapView低,但是我暂时没有找到更好的解决方法。(https://lbs.amap.com/api/android-sdk/guide/create-map/show-map)
问题图:
最终效果:
@InjectView(R.id.amapView)
MapView mBmapView;
//声明AMapLocationClient类对象
private AMapLocationClient mLocationClient = null;
//声明AMapLocationClientOption对象
private AMapLocationClientOption mLocationOption = null;
private AMap mAmap;
private static final int MAP_ZOOM_SIZE = 18;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
mBmapView.onCreate(savedInstanceState);
}
@Override
protected void onPause() {
if (mBmapView != null) {
//在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
mBmapView.onPause();
}
if (mLocationClient != null) {
mLocationClient.stopLocation();
}
super.onPause();
}
@Override
protected void onResume() {
//在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
mBmapView.onResume();
if (mLocationClient != null) {
mLocationClient.startLocation();
}
super.onResume();
}
@Override
protected void onDestroy() {
LogUtils.i("onDestroy");
clearRouteLines();
if (mLocationClient != null) {
mLocationClient.stopLocation();
}
if (mBmapView != null) {
//在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
mBmapView.onDestroy();
}
super.onDestroy();
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
//在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
mBmapView.onSaveInstanceState(outState);
}
private void initMap() {
if (mAmap == null) {
mAmap = mBmapView.getMap();
}
//设置希望展示的地图缩放级别
CameraUpdate mCameraUpdate = CameraUpdateFactory.zoomTo(MAP_ZOOM_SIZE);
mAmap.moveCamera(mCameraUpdate);
//自定义定位蓝点:
MyLocationStyle myLocationStyle = new MyLocationStyle();
myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.location_indicator));
//连续定位、且将视角移动到地图中心点,定位蓝点跟随设备移动。(1秒1次定位)
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);
//设置精度圆圈(隐藏精度圈)
myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));
myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));
//隐藏高德logo
mAmap.getUiSettings().setLogoBottomMargin(-60);
//设置定位蓝点的Style
mAmap.setMyLocationStyle(myLocationStyle);
//初始化定位
mLocationClient = new AMapLocationClient(getApplicationContext());
//设置定位回调监听
mLocationClient.setLocationListener(this);
//初始化AMapLocationClientOption对象
mLocationOption = new AMapLocationClientOption();
//设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//获取最近3s内精度最高的一次定位结果:
//设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。
//如果设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。
mLocationOption.setOnceLocationLatest(true);
//设置是否返回地址信息(默认返回地址信息)
mLocationOption.setNeedAddress(true);
//设置是否允许模拟位置,默认为true,允许模拟位置
mLocationOption.setMockEnable(false);
//关闭缓存机制
mLocationOption.setLocationCacheEnable(false);
mLocationClient.setLocationOption(mLocationOption);
// 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false
mAmap.setMyLocationEnabled(true);
//隐藏右下角的缩放按钮
mAmap.getUiSettings().setZoomControlsEnabled(false);
//设置默认定位按钮不显示,显示自定义定位按钮并设置click事件。
mAmap.getUiSettings().setMyLocationButtonEnabled(false);
//启动定位
mLocationClient.startLocation();
}
隐藏高德地图的定位图标:
//设置默认定位按钮不显示,显示自定义定位按钮并设置click事件。
mAmap.getUiSettings().setMyLocationButtonEnabled(false);
设置图标带阴影的属性elevation(有background才会生效)
<ImageView
android:id="@+id/im_location"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="right"
android:layout_margin="10dp"
android:background="@drawable/roadrescue_location_btn_selector"
android:clickable="true"
android:elevation="2dp"
android:scaleType="center"
android:src="@drawable/location_btn"
app:layout_constraintRight_toRightOf="parent" />
设置自定义定位图标的点击事件
@OnClick({R.id.im_location })
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.im_location:
//自定义定位控件,可以移动到定位点,使用animateCamera就有动画效果,设置希望展示的地图缩放级别
CameraUpdate mCameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, MAP_ZOOM_SIZE);
mAmap.moveCamera(mCameraUpdate);
break;
}
}
二、Fragment里的使用
在onCreateView里创建地图,其他和Activity一样
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mBmapView.onCreate(savedInstanceState);
return mRootView;
}
三、路径规划
已知起点(可以通过onLocationChanged获得)和终点(比如滴滴司机的起始位置),首先画出路径,显示用户marker和司机marker。然后通过handler每隔1分钟从服务器获取司机位置的经纬度,创建一个ArrayList 保存司机要smooth的两点,每次获取新的坐标时就remove掉0,add新坐标,通过SmoothMoveMarker让小车从0滑到1,这样就移动了。
1、画路径,画起点和终点marker
//路线规划
private RouteSearch mRouteSearch;
//这个数组的作用是把规划路线的所有坐标点放进去,便于绘制
private ArrayList<LatLng> latLonArray = new ArrayList<>();
//小车smooth的路线,存放两个点
private ArrayList<LatLng> runArray = new ArrayList<>();
//平滑移动的标记,即小车
private SmoothMoveMarker smoothMarker;
/**
* 已知司机地址,获取司机起始位置经纬度(用于不知道司机经纬度,知道司机起始地址)和行车时间,先画出一
* 个路线图,起点终点出来
* @param cityName
*/
private void getStoreLatlon(String cityName) {
LogUtils.i("StoreyName;" + cityName);
GeocodeSearch geocodeSearch = new GeocodeSearch(this);
geocodeSearch.setOnGeocodeSearchListener(new GeocodeSearch.OnGeocodeSearchListener() {
@Override
public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) {
}
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {
if (i == 1000) {
if (geocodeResult != null && geocodeResult.getGeocodeAddressList() != null
&& geocodeResult.getGeocodeAddressList().size() > 0) {
GeocodeAddress geocodeAddress = geocodeResult.getGeocodeAddressList().get(0);
double latitude = geocodeAddress.getLatLonPoint().getLatitude();//纬度
double longititude = geocodeAddress.getLatLonPoint().getLongitude();//经度
mStoreLatlng = new LatLng(latitude, longititude);
getStoreDriveTime();
LogUtils.i("lgq地理编码:" + geocodeAddress.getAdcode() + "");
LogUtils.i("lgq纬度latitude:" + latitude + "");
LogUtils.i("lgq经度longititude:" + longititude + "");
}
else {
ToastUtil.showMessage(RoadRescueApplyAct.this, "地名出错");
}
}
}
});
GeocodeQuery geocodeQuery = new GeocodeQuery(cityName.trim(), "29");
geocodeSearch.getFromLocationNameAsyn(geocodeQuery);
}
/**
*查询司机到用户的路径
*/
private void getStoreDriveTime() {
mRouteSearch = new RouteSearch(this);
mRouteSearch.setRouteSearchListener(this);
LatLng rescueLatlng = new LatLng(mCurrentRescueOrder.getLat(), mCurrentRescueOrder.getLng());
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(convertToLatLonPoint(rescueLatlng),
convertToLatLonPoint(mStoreLatlng));
RouteSearch.DriveRouteQuery routeQuery = new RouteSearch.DriveRouteQuery(fromAndTo,
RouteSearch.DRIVING_SINGLE_AVOID_CONGESTION, null, null, "");
mRouteSearch.calculateDriveRouteAsyn(routeQuery);
}
//会走到回调方法,选择onDriveRouteSearched进行处理,driverRouteResult里存放了路径所有经纬度点
@Override
public void onDriveRouteSearched(DriveRouteResult driveRouteResult, int i) {
LogUtils.i("i:" + i);
if (i == 1000) {
if (driveRouteResult == null) {
LogUtils.e("driveResult is null");
}
if (!latLonArray.isEmpty()) {
latLonArray.clear();
}
//设置预计等待时间
LogUtils.i("time:" + driveRouteResult.getPaths().get(0).getDuration());
mDriveDuration = driveRouteResult.getPaths().get(0).getDuration();
mDistance = driveRouteResult.getPaths().get(0).getDistance();
Spanned durationText = Html
.fromHtml(getString(R.string.roadrescue_duration, secondsToMinute(mDriveDuration)));
mTvWaitTime.setText(durationText);
//设置距离
Spanned distanceText = Html.fromHtml(getString(R.string.roadrescue_distance, meterToKilometer(mDistance)));
mTvRescueDistance.setText(distanceText);
//如果为出工就不画路径图
if (mCurrentRescueOrder.getStatus() == 1 || mCurrentRescueOrder.getStatus() == 2) {
return;
}
for (DrivePath path : driveRouteResult.getPaths()) {
for (DriveStep step : path.getSteps()) {
for (LatLonPoint point : step.getPolyline()) {
latLonArray.add(convertToLatLng(point));
}
}
}
//规划路线,画出起点,小车,路线图
drawingRouteOverlay(latLonArray);
//将改点作为未来小车要smooth的起点
if (runArray.size() == 0) {
runArray.add(mStoreLatlng);
}
showCarMarker(mStoreLatlng);
}
else {
LogUtils.e("no route to find");
}
}
/**
* 绘制路线
* @param drivePath
*/
private void drawingRouteOverlay(List<LatLng> drivePath) {
this.startLatLng = drivePath.get(0);
this.endLatLng = drivePath.get(drivePath.size() - 1);
polyLineOptions = new PolylineOptions();
polyLineOptions.width(width);
polyLineOptions.setCustomTexture(BitmapDescriptorFactory.fromResource(R.drawable.custtexture));
endBitmap = BitmapDescriptorFactory.fromResource(R.drawable.location_savecar);
//将客户位置作为起点标记在map上
addMarkerToMap(drivePath);
//画路线
drawLine(drivePath);
}
/**
* 将起点终点的marker添加到地图上
*/
public void addMarkerToMap(List<LatLng> drivePath) {
try {
//清除原来的marker
mAmap.clear();
//将用户的位置marker添加到地图上
LatLng rescueLatLng = new LatLng(mCurrentRescueOrder.getLat(), mCurrentRescueOrder.getLng());
endMarker = mAmap.addMarker(new MarkerOptions().position(rescueLatLng).icon(endBitmap));
}
catch (Throwable throwable) {
throwable.printStackTrace();
}
}
/**
* 画线
*/
private void drawLine(List<LatLng> drivePath) {
try {
polyLineOptions.addAll(drivePath);
allPolyLines.add(mAmap.addPolyline(polyLineOptions));
}
catch (Throwable e) {
e.printStackTrace();
}
}
//显示司机所在位置
private void showCarMarker(LatLng location) {
zoomToSpan();
if (rescueCarMarker == null) {
//如果是空的添加一个新的icon方法就是设置定位图标,可以自定义
rescueCarMarker = mAmap.addMarker(new MarkerOptions().position(location)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.savecar)));
}
else {
rescueCarMarker.setPosition(location);
}
}
/**
* 将点始终保持在能看到的位置
*/
private void zoomToSpan() {
try {
LatLngBounds.Builder b = LatLngBounds.builder();
b.include(new LatLng(startLatLng.latitude, startLatLng.longitude));
b.include(new LatLng(endLatLng.latitude, endLatLng.longitude));
LatLngBounds bounds = b.build();
//就是这个方法把视觉一直控制在大概中间的位置,具体的可以看高德地图API
mAmap.animateCamera(CameraUpdateFactory.newLatLngBoundsRect(bounds, 100, 100, 100, 450));
}
catch (Throwable e) {
e.printStackTrace();
}
}
2、更新司机marker位置
//每隔1分钟从服务器获取司机经纬度
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_RESCUE_CAR_LOCATION,
UPDATE_RESCUE_CAR_LOCATION_INTERNAL);
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (runArray == null) {
return;
}if (msg.what == MSG_UPDATE_RESCUE_CAR_LOCATION) {
//获取司机位置
getRescueCarDistance(mCurrentRescueOrder.getOrderId());
sendEmptyMessageDelayed(MSG_UPDATE_RESCUE_CAR_LOCATION, UPDATE_RESCUE_CAR_LOCATION_INTERNAL);
}
}
};
/**
* 获取司机位置并计算距离(已出动)
* @param orderId
*/
private void getRescueCarDistance(long orderId) {
RequestManager2.getServiceInstance()
.getRescuevehicleLocation(orderId)
.compose(RequestManager2.<LatLng> setSchedulers())
.subscribe(new BackgroundSubscriber<LatLng>(RoadRescueApplyAct.this) {
@Override
public void onSuccess(LatLng latLng) {
super.onSuccess(latLng);
LogUtils.e("rescuecar,latLng:" + latLng);
//获取销售店与用户的距离,时间由地图map来算
LatLng rescueLatlng = new LatLng(mCurrentRescueOrder.getLat(), mCurrentRescueOrder.getLng());
mDistance = AMapUtils.calculateLineDistance(rescueLatlng, latLng);
Spanned distanceText = Html
.fromHtml(getString(R.string.roadrescue_distance, meterToKilometer(mDistance)));
mTvRescueDistance.setText(distanceText);
//添加Marker显示小车位置
if (latLng.longitude == 0 || latLng.latitude == 0) {
return;
}
//更新小车位置
if (runArray.size() == 1) {
//如果runArray里只有一个点,获取小车最新位置后再加一个点
runArray.add(latLng);
}
else if (runArray.size() == 2) {
//如果runArray里已经有两个点,移除第一个点,加入最新的点
runArray.remove(0);
runArray.add(latLng);
}
if (smoothMarker != null) {
smoothMarker.destroy();
}
//让小车从runArray 0滑动到1
showCarMarkerSmooth();
}
});
}
//滑动小车
private void showCarMarkerSmooth() {
// 获取轨迹坐标点
zoomToSpan();
if(rescueCarMarker!=null){
//移除一开始设置的小车marker
rescueCarMarker.remove();
}
smoothMarker = new SmoothMoveMarker(mAmap);
// 设置滑动的图标
smoothMarker.setDescriptor(BitmapDescriptorFactory.fromResource(R.drawable.savecar));
LatLng drivePoint = runArray.get(0);
Pair<Integer, LatLng> pair = SpatialRelationUtil.calShortestDistancePoint(runArray, drivePoint);
runArray.set(pair.first, drivePoint);
List<LatLng> subList = runArray.subList(pair.first, runArray.size());
// 设置滑动的轨迹坐标点
smoothMarker.setPoints(subList);
// 设置滑动的总时间60s
smoothMarker.setTotalDuration(60);
// 开始滑动
smoothMarker.startSmoothMove();
}
但是实际司机的位置数据可能没有,所以我们可以模拟司机运动,数据就从获取的路径的所有经纬度点里面选取,模拟司机一直沿着规划的路线走。定义两个ArrayList,一个是latLonArray存放规划路径的所有点,一个是runArray存放两个点,不断从latLonArray里面取点,模拟更新司机前进。(比如latLongArray里的点是0,1,2,3,4…,runArray更新数据(0,1)(1,2)(2,3)…然后小车marker根据runArray里面的两个点不断往前滑动)
/**
* 根据起点和终点的经纬度查询路线
*/
private void startRouteSearch() {
LogUtils.e("startRouteSearch");
mRouteSearch = new RouteSearch(this);
mRouteSearch.setRouteSearchListener(this);
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(mStarPoint, mEndPoint);
RouteSearch.DriveRouteQuery routeQuery = new RouteSearch.DriveRouteQuery(fromAndTo,
RouteSearch.DRIVING_SINGLE_AVOID_CONGESTION, null, null, "");
mRouteSearch.calculateDriveRouteAsyn(routeQuery);
}
@Override
public void onDriveRouteSearched(DriveRouteResult driveRouteResult, int i) {
LogUtils.i("i:" + i);
if (i == 1000) {
if (driveRouteResult == null) {
LogUtils.e("driveResult is null");
}
if (!latLonArray.isEmpty()) {
latLonArray.clear();
}
for (DrivePath path : driveRouteResult.getPaths()) {
for (DriveStep step : path.getSteps()) {
for (LatLonPoint point : step.getPolyline()) {
latLonArray.add(convertToLatLng(point));
}
}
}
//规划路线,画出起点,小车,路线图
drawingRouteOverlay(latLonArray);
runArray.add(latLonArray.get(0));
runArray.add(latLonArray.get(1));
showCarMarkerSmooth();
mHandler.sendEmptyMessage(MSG_UPDATE_CAR);
}
else {
LogUtils.e("no route to find");
}
}
private void showCarMarkerSmooth() {
zoomToSpan();
smoothMarker = new SmoothMoveMarker(mAmap);
// 设置滑动的图标
smoothMarker.setDescriptor(BitmapDescriptorFactory.fromResource(R.drawable.car));
LatLng drivePoint = runArray.get(0);
Pair<Integer, LatLng> pair = SpatialRelationUtil.calShortestDistancePoint(runArray, drivePoint);
runArray.set(pair.first, drivePoint);
List<LatLng> subList = runArray.subList(pair.first, runArray.size());
// 设置滑动的轨迹坐标点
smoothMarker.setPoints(subList);
// 设置滑动的总时间
smoothMarker.setTotalDuration(1);
// 开始滑动
smoothMarker.startSmoothMove();
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (runArray == null) {
return;
}
if (msg.what == MSG_UPDATE_CAR) {
if (latLonArray.size() > 2) {
if (runArray.size() > 0) {
runArray.remove(0);
runArray.add(latLonArray.remove(0));
}
else {
runArray.add(latLonArray.remove(0));
runArray.add(latLonArray.remove(0));
}
if (smoothMarker != null) {
smoothMarker.destroy();
}
showCarMarkerSmooth();
sendEmptyMessageDelayed(MSG_UPDATE_CAR, UPDATE_CAR_INTERNAL);
}
else {
LogUtils.e("stop");
smoothMarker.stopMove();
clearRouteLines();
smoothMarker.destroy();
removeMessages(MSG_UPDATE_CAR);
}
}
};