高德地圖場景使用技巧—絕對實用

轉載請註明出處: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地圖隱藏掉,顯示出導航地圖,開始導航!簡直完美!

菜鳥一隻,如有不對之處請指出。您的鼓勵是我寫作的最大動力!

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