高德地图场景使用技巧—绝对实用

转载请注明出处:https://blog.csdn.net/Dreamer_man/article/details/105895091

最近公司项目收尾了,正好趁着这段空闲时间,总结一下项目开发过程中遇到的一些问题。希望大家在垒代码的过程中有所启发,以下讲述中可能是大家项目中遇到的问题,也有可能是高德的BUG,不管是哪一类,还是老样子废话较多, 大家挑着有用的看,没用的略过就行。

1、手指滑动地图,自动跳回定位中心点(问题)

在这里插入图片描述
相信不少小伙伴在制作地图的时候都遇到过这个问题,无论怎么晃动地图,最终都会回到定位原点。真是气急败坏,又无奈。其实这个问题很简单,在这里咱们先当一道开胃菜来解决。

1.1 问题场景—初始化地图时,将定位点设置成地图中心。

当进入地图后,很多人都希望地图的视野是自己当前的位置,并且把当前定位点设置为地图中心点,但是如果处理不当就容易导致视频中的现象。

1.2 问题原因

问题代码
public void onLocationChanged(AMapLocation aMapLocation) {
    if (aMapLocation != null) {
        if (aMapLocation.getErrorCode() == 0) {
            LatLng mapLocationLatLng = new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude());
            mAMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.amap_start)).position(mapLocationLatLng));
            //设置缩放级别
            mAMap.moveCamera(CameraUpdateFactory.zoomTo(21));
            //将地图移动到定位点
            mAMap.moveCamera(CameraUpdateFactory.changeLatLng(mapLocationLatLng));
        }
    }
}

这个问题的起源点就在于AMap的moveCamera(CameraUpdateFactory.changeLatLng(locationLatLng))方法。

moveCamera(CameraUpdateFactory.changeLatLng(locationLatLng)):移动视图到定位原点。

这个方法本来是没有任何问题的,但是用在onLocationChanged()这个回调方法里,可要小心了。高德地图默认每隔2秒定位一次,简单来说就是,每隔2秒就会调用onLocationChanged()这个方法。也就是说每隔2秒就会调用moveCamera(),从而导致地图划至定位中心点。

1.3 解决办法

很明显是moveCamera()执行多次导致出现了问题,实际应用中我们只需要执行一次就可以了。因此可以设置一个布尔值来控制执行次数。

private boolean isFirstLocation = false;
public void onLocationChanged(AMapLocation aMapLocation) {
    if (aMapLocation != null) {
        if (aMapLocation.getErrorCode() == 0) {
            if (!isFirstLocation) {
                isFirstLocation = true;
                LatLng mapLocationLatLng = new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude());
                mAMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.amap_start)).position(mapLocationLatLng));
                //设置缩放级别
                mAMap.moveCamera(CameraUpdateFactory.zoomTo(21));
                //将地图移动到定位点
                mAMap.moveCamera(CameraUpdateFactory.changeLatLng(mapLocationLatLng));
            }
        }
    }
}

2、定位点信息获取异常(BUG)

这是继问题一而延伸出来的另一个问题,当然这不是问题一导致的,而是科学的自然反应,你也可以理解为BUG。因为业务需求,小伙伴们可能需要在onLocationChanged()回调方法中,获取定位点的信息,比如定位城市,从而更新UI。但是有时候这些信息获取为空,相应的UI就会出现异常。我们需要做的就是,寻求事情发展的真相、科学发展的规律,有则举报,无则加勉。

2.1 问题场景—获取定位点信息,更新UI

当初入地图,我们需要获取到定位点的信息,例如:定位城市、定位座标、定位地址等信息,来满足业务需求,但是有时候事与愿违,定位信息就是拿不到,没办法代码就是这么神奇,不要问,问就是看代码,代码会说谎吗?

2.2 问题原因

讲了那么多废话,这里就揭晓原因,问题就出在setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy) 方法,啊哈!?是不是很神奇,相信有很多小伙伴有很多问号。很多人都没有用过这个方法,就算用到了也不知道干什么的。

setLocationMode(AMapLocationMode mode):设置定位模式
定位模式:
1.Hight_Accuracy:会同时使用网络定位和GPS定位,优先返回最高精度的定位结果,以及对应的地址描述信息
2.Battery_Saving:不会使用GPS和其他传感器,只会使用网络定位(Wi-Fi和基站定位)
3.Device_Sensors:不需要连接网络,只使用GPS进行定位,这种模式下不支持室内环境的定位,需要在室外环境下才可以成功定位

一顿复制粘贴,官网上说的已经很清楚了,我们一般会优先选择Hight_Accuracy,因为在用户实际应用场景下,你并不知道用户是在室内还是室外,所处位置信号弱还是信号强。所以选择高精度定位模式可以适用大部分应用场景。

那么问题就来了,经过我上千次的重复实验和测试,摸爬滚打终于找到了规律和其中的BUG。第一、低功耗定位模式下,定位信息可以实时拿到!定位模式设置成低功耗定位模式(Battery_Saving),也就是通过网络定位,这些定位信息都是可以实时拿到,不会出半点差错(这有可能跟网络速度有关系,有兴趣的童鞋可以自行测试一下,寡人这里网络太强了不方便测试)。第二、仅设备定位模式下,定位信息偶现获取异常!定位模式设置成Device_Sensors,也就是通过GPS定位,神奇的事情就发生了,定位信息出现了异常,拿到的都是空数据,但是过了几秒钟后就又恢复了!

这是为什么呢?我猜想这是GPS定位刚激活时的一个BUG,可能GPS定位启动时信号比较弱,需要一段时间缓冲校准,等定位精准后,定位信息就有了!(以上仅为猜测,没有实际理论根据)。

2.3 解决办法1

setLocationMode方法参数设置成低功耗定位模式(Battery_Saving)。这样方式虽然可以回避定位信息点获取异常的办法,但是不推荐,因为就像上面说过的,你永远不知道用户的真实使用场景,如果在没有网络的情况下,用户将无法完成定位功能。当然如果你的业务需求是,在仅通过网络实现定位的话另当别论。

2.4 解决方法2

判断一下定位信息是否为空,如果为空则return,不为空则获取定位信息。代码是死的,但是头脑是灵活的,发散人类的思维,就可以回避以上问题。通过AMapLocation对象获取当前所在城市信息,如果为空则返回,不执行下面的UI操作,直到获得到城市信息再进行UI更新!大家注意了,为什么要选择判断城市名称信息,而不是选择街道名称?小区名称?这些详细的定位信息!因为经过实验验证,在一些偏远的地区,这些详细的定位信息也是获得不到的,因此具有"低容错率"。所以我们选择判断城市名称信息是否为空,但是我们不能保证,未来在其他情况下城市信息会不会为空,因此我们不能绝对解决问题,但是可以回避绝大部分错误的可能,真正要解决问题还是需要修复这个BUG。。。有点绕了,简单来说,就按照我的方法来干就得了。

private boolean isFirstLocation = false;
public void onLocationChanged(AMapLocation aMapLocation) {
    if (aMapLocation != null) {
        if (aMapLocation.getCity().equals(""))
            return;

        if (aMapLocation.getErrorCode() == 0) {
            if (!isFirstLocation) {
                isFirstLocation = true;
                LatLng mapLocationLatLng = new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude());
                mAMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.amap_start)).position(mapLocationLatLng));
                //设置缩放级别
                mAMap.moveCamera(CameraUpdateFactory.zoomTo(21));
                //将地图移动到定位点
                mAMap.moveCamera(CameraUpdateFactory.changeLatLng(mapLocationLatLng));
            }
        }
    }
}

3、动态缩放导航地图失效-animateCamera方法(BUG)

这个问题困扰我很久,真的恶心到我了。

在这里插入图片描述

3.1 问题场景—导航地图里,进行地图动态缩放。

相信不少小伙伴们为了方便,仅调用导航这一个SDK,来实现地图的所有功能,包括地图,定位,绘制路线,导航等。开始时我也是这个思路,正常来说这个思路是完全没有问题的。但是在路线制定的时候出现了问题,使我不得不往麻烦的路线出发。在绘制路线的时候,往往会调用animateCamera()方法,来控制视图中路线的范围,但是在导航地图中,因为animateCamera()方法有时会失效,导致路线视图经常超出视野范围。(希望官方可以尽早解决这个BUG)

3.2 问题原因

测试代码
private AMapNaviView aMapNaviView;
private AMap aMap;
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.navi);
    aMapNaviView = findViewById(R.id.naviView);
    aMapNaviView.onCreate(savedInstanceState);
    aMap = aMapNaviView.getMap();
    findViewById(R.id.navi_change_btn).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(NaviActivity.this, "开始动画", Toast.LENGTH_SHORT).show();
            aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(aMapNaviLocation.getCoord().getLatitude(),
                                                                            aMapNaviLocation.getCoord().getLongitude()), 5), 1000, null);
        }
    });
    findViewById(R.id.navi_recover_btn).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            aMap.moveCamera(CameraUpdateFactory.zoomTo(20));
        }
    });
}

在这里我们先科普一下高德地图的基础知识,高德一共有两种类型的地图,一种是2D地图,这种地图在导航应用中不太常用。另一种是3D地图,我们一般会首选这个地图,那么这两种地图有什么区别呢,emmm…顾名思义,一个2D,一个3D…

这个3D地图的AMap对象竟然可以通过两种方式获得!小伙伴们你们是不是有很多问号?那什么是AMap?这个就是地图的接口,通过这个对象可以操作地图大部分UI,也就是万恶之源。AMap第一种获取方式是通过3D地图SDK的MapView方式获取,这个也是大家经常使用的获取方式,但是在3D地图SDk中是不包括导航功能的,因此需要导航的小伙伴还需要导航SDK,这也是AMap的第二种获取方式。第二种获取方式是通过导航SDK的AMapNaviView来获取,导航SDK体积很庞大,他即集成了3D地图的绝大部分功能,又集成了导航的功能!那么问题来了,既然有导航SDK这么无敌的存在,我要这3D地图SDK有何用?相信不少小伙伴们已经发现了。

在这里我总结一下,主要有两个原因

1、导航地图体积太过庞大,对于不需要导航功能的小伙伴们来说就是画蛇添足,没必要牺牲apk的大小,来达到装b的目的。
2、3D地图是始祖,包含了所有地图功能,已经很完善了。而导航SDK是弟弟,他窃取了祖先的大部分功能,然后还添加了导航功能,但是为了兼容必须要牺牲一些地图功能的性能,所以可能会导致一些不知名的BUG产生!

导航地图中的animateCamera()方法偶尔会失效,导致无法动态缩放地图。

animateCamera(CameraUpdate update,long durationMs,AMap.CancelableCallback cancelableCallback):按照指定的动画时长及传入的CameraUpdate参数更新地图状态,同时设置一个cancelableCallback来监听动画执行的结果。
参数:
update - 地图状态将要发生的变化。
durationMs - 动画的持续时间,单位毫秒。
cancelableCallback - 如果动画正常完成,则会回调onFinish()方法,被中断时回调onCancel()。

3.3 解决办法1

当然还是那句话,代码是死的,但是人是活的!我们完全可以回避这个BUG。

通过moveCamera()方法代替animateCamera()方法。moveCamera方法和animateCamera方法的区别就在于缩放过程中是否有动画!当然顾名思义,大家也能猜到哪个方法会用到动画!当然这也是委曲求全的一种,但是对于强迫症来说,没有这过渡动画实在太难受了!那就试试下面这种办法!

3.4 解决办法2

在3D地图里实现路线动态缩放,然后在导航地图中实现导航。经过我的验证测试,3D地图中animateCamera()是没有任何问题的,为了达到以假乱真的效果,还需要下面这种布局添油加醋。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.amap.api.navi.AMapNaviView
        android:id="@+id/naviView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <com.amap.api.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

因为是相对布局,下面的mapView会掩盖上面的naviView,实现初入地图,仅仅只有3D地图显示的人类迷惑行为。这时候可以在3D地图中进行各种骚操作,比如绘制路线,并且通过animateCamera方法,使路线所有点显示在屏幕视野内。画完路线,再来一手妙手回春,调用setVisiable(VIEW.GONE)方法,将3D地图隐藏掉,显示出导航地图,开始导航!简直完美!

菜鸟一只,如有不对之处请指出。您的鼓励是我写作的最大动力!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章