自制導航App(包含地圖、定位、自定義marker、路線制定、模擬導航等功能)

高德地圖路線導航製作

轉載請註明出處:https://blog.csdn.net/Dreamer_man/article/details/104193832

由於公司項目需求,特地去官網,重新溫習了一遍高德的地圖製作。並且自己寫了個Demo,主要包含5大功能:地圖、定位、自定義marker、路線制定、模擬導航。下面是效果圖(代碼下載鏈接在最下面,有需要的拿走):

在這裏插入圖片描述

1. 準備工作:

首先需要做一些地圖的準備工作,這就好比寫代碼前,要洗手通風一樣。

1.1 獲取Key

第一步,去高德官網申請key,具體申請方式,獲取key已經講的很清楚了,這裏我就不細細道來了。

圖1-1 申請Key

在這裏插入圖片描述

1.2 添加SDK

第二步,添加SDK。這裏有兩種添加方式,第一種是通過拷貝添加SDK,第二種是通過Gradle集成SDK。這裏我選擇的是第二種,當然也建議大家用第二種,爲什麼呢?因爲懶,哈哈,當然具體需要集成什麼SDK,根據大家業務需求寫。如果小夥伴們對第一種也感興趣,可以參考添加SDK

//3D地圖so及jar和導航
implementation 'com.amap.api:navi-3dmap:latest.integration'
//定位功能
implementation 'com.amap.api:location:latest.integration'
//搜索功能
implementation 'com.amap.api:search:latest.integration'
注意(此乃官方吐槽):

1. navi導航SDK 5.0.0以後版本包含了3D地圖SDK,所以請不要同時引入 map3d 和 navi SDK。

2. 依照上述方法引入 SDK 以後,不需要在libs文件夾下導入對應SDK的 so 和 jar 包,會有衝突。

第2點說人話就是,如果在gradle中添加了上面幾個依賴後,就不需要添加其他地圖so庫和jar包(手動滑稽,是不是很方便)

1.3 配置AndroidManifest.xml

第三步,配置AndroidManifest.xml

首先,聲明權限

<!--地圖包、搜索包需要的基礎權限-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允許程序設置內置sd卡的寫權限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允許程序獲取網絡狀態-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允許程序訪問WiFi網絡信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允許程序讀寫手機狀態和身份-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允許程序訪問CellID或WiFi熱點來獲取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--用於訪問GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--這個權限用於獲取wifi的獲取權限,wifi信息會用來進行網絡定位-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--這個權限用於允許程序在手機屏幕關閉後後臺進程仍然運行-->
<uses-permission android:name="android.permission.WAKE_LOCK"/>

然後,設置高德key

<meta-data   
    android:name="com.amap.api.v2.apikey"                
    android:value="開發者申請的key"/>

最後,添加定位服務(這點很重要,沒有這個服務,應用無法定位)

<service android:name="com.amap.api.location.APSService"></service>

完事具備,只欠東風,咱們就開始壘代碼。

2. 地圖顯示

第一步,在XML文件中定義MapView控件

<com.amap.api.maps.MapView
      android:id="@+id/mapView"
      android:layout_width="match_parent"   
      android:layout_height="match_parent"/>

第二步,再初始化地圖,

mapView = findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
if (aMap == null) {    
    aMap = mapView.getMap();
}
注意:

1. mapView.onCreate(savedInstanceState)一定要加上,否則地圖無法顯示。

2. 在activity生命週期中,對mapView進行相應的處理,demo中有體現。

3. 實時定位

第一步,初始化定位參數,設置定位監聽(代碼中都有詳細的註釋)

MyLocationStyle myLocationStyle = new MyLocationStyle();
//設置連續定位模式下的定位間隔,只在連續定位模式下生效,單次定位模式下不會生效。單位爲毫秒。
myLocationStyle.interval(2000);
//定位藍點展現模式,默認是LOCATION_TYPE_LOCATION_ROTATE
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);
//設置是否顯示定位小藍點,用於滿足只想使用定位,不想使用定位小藍點的場景,設置false以後圖面上不再有定位藍點的概念,但是會持續回調位置信息。
myLocationStyle.showMyLocation(true);
//設置定位藍點的Style
aMap.setMyLocationStyle(myLocationStyle);
// 設置定位監聽
aMap.setLocationSource(this);
//設置爲true表示啓動顯示定位藍點,false表示隱藏定位藍點並不進行定位,默認是false。
aMap.setMyLocationEnabled(true);
// 設置地圖模式,aMap是地圖控制器對象。1.MAP_TYPE_NAVI:導航地圖 2.MAP_TYPE_NIGHT:夜景地圖 3.MAP_TYPE_NORMAL:白晝地圖(即普通地圖) 4.MAP_TYPE_SATELLITE:衛星圖
aMap.setMapType(AMap.MAP_TYPE_NORMAL);
//設置默認定位按鈕是否顯示,非必需設置。
aMap.getUiSettings().setMyLocationButtonEnabled(true);
//控制比例尺控件是否顯示,非必須設置。
aMap.getUiSettings().setScaleControlsEnabled(true);

第二步,實現AMap.setLocationSource監聽器,並且回調activate()和deactivate()兩個方法。activate()方法是在激活定位的時候觸發,需要在裏面初始化定位參數,並開始定位。deactivate()方法是在定位停止的時候觸發,需要在方法裏停止定位,避免不必要的資源浪費。

/**
 * 激活定位
 */
@Override
public void activate(OnLocationChangedListener onLocationChangedListener) {
    mListener = onLocationChangedListener;
    if (aMapLocationClient == null) {
        //初始化定位
        aMapLocationClient = new AMapLocationClient(this);
        //初始化定位參數
        aMapLocationClientOption = new AMapLocationClientOption();
        //設置定位回調監聽
        aMapLocationClient.setLocationListener(this);
        //設置爲高精度定位模式
        aMapLocationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
        //設置定位參數
        aMapLocationClient.setLocationOption(aMapLocationClientOption);
        //啓動定位
        aMapLocationClient.startLocation();
    }
}   
/**
 * 停止定位
 */
@Override
public void deactivate() {
    mListener = null;
    if (aMapLocationClient != null) {
        aMapLocationClient.stopLocation();
        aMapLocationClient.onDestroy();
    }
    aMapLocationClient = null;
}

第三步,在定位回調中設置顯示定位小藍點,isFirstLocationn的作用是防止拖動地圖後,定位小藍點老是返回到屏幕的中心位置。

public void onLocationChanged(AMapLocation aMapLocation) {
    if (mListener != null && aMapLocation != null) {
        this.aMapLocation = aMapLocation;
        if (aMapLocation.getErrorCode() == 0) {
            if (isFirstLocation) {
                aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude())));
                mListener.onLocationChanged(aMapLocation);// 顯示系統小藍點
                isFirstLocation = false;
            }
        } else {
            Log.e("TAG", "定位失敗!!!");
        }
    }
}
注意:

1. setMyLocationEnabled(true)方法必須在setLocationSource(this)定位監聽之後執行,否則定位會失效。

2. AMap.getUiSettings()是獲得高德地圖控件對象,可以通過這個方法設置地圖控件

4. 自定義marker

第一步,在XML文件中定義marker佈局(僅爲demo,具體樣式根據個人需求定製)

<Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="路線製作"/>
<Button
    android:id="@+id/btn2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="開始導航"/>

第二步,實現AMap.setInfoWindowAdapter()的監聽,回調 getInfoWindow()和getInfoContent()兩個方法,具體這兩個方法有什麼區別,繪製自定義marker已經講的很詳細了,我就不細說了,主要區別在於getInfoContent()不能修改整個 InfoWindow 的背景和邊框,無論自定義的樣式是什麼樣,SDK 都會在最外層添加一個默認的邊框。 在這裏我實現了getInfoWindow()方法。

public View getInfoWindow(Marker marker) {
    if (infoView == null) {
        infoView = LayoutInflater.from(this).inflate(R.layout.marker_info_window, null);
    }
    render(marker, infoView);
    return infoView;
}
private void render(final Marker marker, View infoView) {
    infoView.findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
           
        }
    });
    infoView.findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        
        }
    });
}

5.路線制定

第一步,初始化RouteSearch對象,並且設置查詢結果的回調監聽器

routeSearch = new RouteSearch(this);
routeSearch.setRouteSearchListener(this);

第二步,需要確定起點和終點,畢竟兩點確定一條線。

startLatLonPoint = new LatLonPoint(aMapLocation.getLatitude(), aMapLocation.getLongitude());
endLatLonPoint = new LatLonPoint(marker.getPosition().latitude, marker.getPosition().longitude);
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(startLatLonPoint, endLatLonPoint);

第三步,選擇路線,有駕車線路、步行線路、公交線路、騎行線路、貨車線路。爲了方便展示,我這裏實現了步行線路,具體什麼線路,根據個人需求進行選擇。

RouteSearch.WalkRouteQuery query = new RouteSearch.WalkRouteQuery(fromAndTo, RouteSearch.WALK_DEFAULT);

第四步,計算路線,至於如何計算,大家大可不用關心,這些計算方法高德已經封裝好接口了,咱們拿來直接用就好。

routeSearch.calculateWalkRouteAsyn(query);

第五步,根據計算結果,畫出路線

public void onWalkRouteSearched(WalkRouteResult walkRouteResult, int i) {
    aMap.clear();
    if (i == AMapException.CODE_AMAP_SUCCESS) {
        if (walkRouteResult != null && walkRouteResult.getPaths() != null) {
            if (walkRouteResult.getPaths().size() > 0) {
                routeResult = walkRouteResult;
                final WalkPath walkPath = walkRouteResult.getPaths().get(0);
                if (walkPath == null) {
                    return;
                }
                WalkRouteOverlay overlay = new WalkRouteOverlay(
                        this, aMap, walkPath,
                        walkRouteResult.getStartPos(),
                        walkRouteResult.getTargetPos());
                overlay.removeFromMap();
                overlay.addToMap();
                overlay.zoomToSpan();
            }
        }
    }
}
注意:

1. 地圖SDK V4.1.3版本開始,SDK不再提供 com.amap.api.maps.overlay 包下的 overlay,已在官方demo中開源。如果只有行走路線的話,需要下面這幾個類。

圖5-1行走路線的overlay

在這裏插入圖片描述

6. 模擬導航

在這先解釋一下什麼是模擬導航,模擬導航就是真實模擬實時導航的情況,比如A爲起點,B爲終點,實時導航需要你從A點走到B點,而模擬導航不需要你移動,它可以模擬移動,自動從A點走到B點。當然這只是爲了方便展示,真實情景還是需要實時導航的,不過只需要改變AMapNavi.startNavi()方法的參數即可,詳細情節後面會有敘述。

第一步,在XML文件中定義AMapNaviView控件

<com.amap.api.navi.AMapNaviView
    android:id="@+id/naviView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</com.amap.api.navi.AMapNaviView>

第二步,獲取 AMapNaviView實例,並設置監聽。

aMapNaviView = findViewById(R.id.naviView);aMapNaviView.setAMapNaviViewListener(this);aMapNaviView.onCreate(savedInstanceState); 

第三步,獲取AMapNavi實例,並設置監聽

//獲取AMapNavi實例
aMapNavi = AMapNavi.getInstance(getApplicationContext());
//添加監聽回調,用於處理算路成功
aMapNavi.addAMapNaviListener(this);

第四步,計算步行規劃路線,AMapNavi對象初始化成功後,會觸發onInitNaviSuccess方法。

public void onInitNaviSuccess() {
    aMapNavi.calculateWalkRoute(startNaviLatLng, endNaviLatLng);
}

第五步,開始導航,路線規劃成功後,會觸發onCalculateRouteSuccess()方法,在這裏咱們開始導航。

public void onCalculateRouteSuccess(int[] ints) {
    aMapNavi.startNavi(NaviType.EMULATOR);
}
注意:

1. NaviView與MapView一樣,要根據Activity的生命週期來進行相應處理,demo中有體現,在這裏提別提醒一下在Activity銷燬的時候調用AMapNavi的stopNavi()和destory()方法,來停止導航,否則再次導航時會出現AMapNavi初始化失敗的問題!

protected void onDestroy() {
    super.onDestroy();
    mAMapNaviView.onDestroy();
    //since 1.6.0 不再在naviview destroy的時候自動執行AMapNavi.stopNavi();請自行執行
    mAMapNavi.stopNavi();
    mAMapNavi.destroy();
}
2. 如果需要導航語音的話,調用setUseInnerVoice()方法即可實現。
aMapNavi.setUseInnerVoice(true);

到這裏一款導航app的雛形就做好了,如果還想要功能變得更強大,只需添枝加葉即可。

導航appDemo:
https://download.csdn.net/download/Dreamer_man/12138974

菜鳥一隻,如有不對之處請指出。您的鼓勵是我寫作的最大動力!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章