自制导航App(包含地图、定位、自定义marker、路线制定、模拟导航等功能)

高德地图路线导航制作

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

由于公司项目需求,特地去官网,重新温习了一遍高德的地图制作。并且自己写了个Demo,主要包含5大功能:地图、定位、自定义marker、路线制定、模拟导航。下面是效果图(代码下载链接在最下面,有需要的拿走):

1. 准备工作:

首先需要做一些地图的准备工作,这就好比写代码前,要洗手通风一样。

1.1 获取Key

第一步,去高德官网申请key,具体申请方式,获取key已经讲的很清楚了,这里我就不细细道来了。

图1-1 申请Key

自制导航App(包含地图、定位、自定义marker、路线制定、模拟导航等功能)_第1张图片

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

自制导航App(包含地图、定位、自定义marker、路线制定、模拟导航等功能)_第2张图片

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

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

你可能感兴趣的:(android,java,定位,gps,街景地图)