跳过导入高德地图,导入包,直接到使用

一、在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)

问题图:

android 高德地图marker拖动 高德地图mapview_android


最终效果:

android 高德地图marker拖动 高德地图mapview_自定义_02

@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);
                }
        }
    };