接着上一篇的定位写,之前的那篇文章仅仅介绍了简单的当前位置的定位,其实官方文档写的很详细了,出贴意义不大,不过导航篇还是有必要叨叨一下的,在我最初研究百度地图API的时候,差点被官方文档绕迷糊了,还是踩过许多坑的,也许是我悟性不够吧,哈哈,下面出教程(其实更偏向于我的私人笔记)
还是熟悉的–准备工作
由于这个是接着上一篇写的,代码也是在原来工程的基础上加的功能,所以,之前强调的准备工作依然需要的,在这里就不多说了。提醒一下,如果在之前定位实现的时候下载的SDK没有包含导航模块,一定要重新去百度地图API下载区去下载完整的SDK包,把之前的替换掉。
相对于定位,导航新加了一些权限,如下:
<!-- 百度地图导航所需权限 start -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.BAIDU_LOCATION_SERVICE" />
<uses-permission android:name="android.permission.ACCES_MOCK_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<!-- 来电消音 -->
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 摄影机 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 百度地图导航所需权限 end -->
还要加上百度地图的服务声明(其实这些在官方文档介绍的都很详细的)
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote" />
在之前的SDK版本中,语音TTS需要设置鉴权白名单的,因为要写这篇帖子,昨天就重新看了下官方文档,发现方法变了,不需要设置白名单了,新版本需要两个步骤,一、在http://yuyin.baidu.com/app创建自己的应用,注册语音合成功能;二、在初始化导航前,调用APPID的设置接口,这样就可以进行语音导航了,所谓APPID就是在注册语音合成功能的时候生成的,在步骤一的新建完的应用中可以查到
Bundle bundle = new Bundle();
// 必须设置APPID,否则会静音
bundle.putString(BNCommonSettingParam.TTS_APP_ID, "你自己的APPID");
BNaviSettingManager.setNaviSdkParam(bundle);
BaiduNaviOperation类
先新建一个百度地图导航的操作类,当然啦,你大可不必一定要新建类,个人习惯而已,这个类里主要包含两个内容,首先是导航引擎的初始化,然后就是发起算路及算路成功后跳转到诱导View
导航引擎初始化
初始化之前要检查是否有完备的权限
// 申请权限
if (android.os.Build.VERSION.SDK_INT >= 23) {
// 保证导航功能完备
if (!hasBasePhoneAuth()) {
if (!hasRequestComAuth) {
hasRequestComAuth = true;
mActivity.requestPermissions(authComArr, authComRequestCode);
return;
} else {
Toast.makeText(mActivity, "没有完备的权限!", Toast.LENGTH_SHORT).show();
}
}
}
权限完备之后,要设置一个用来缓存导航数据的文件夹,在这里我设置为根文件夹下名为“导航demo”的文件夹,不存在则新建
private boolean initDirs() {
mSDCardPath = getSdcardDir();
if (mSDCardPath == null) {
return false;
}
File f = new File(mSDCardPath, APP_FOLDER_NAME);//APP_FOLDER_NAME是文件夹的名字,自定义的
if (!f.exists()) {
try {
f.mkdir();
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
return true;
}
private String getSdcardDir() {
if (Environment.getExternalStorageState().equalsIgnoreCase(
Environment.MEDIA_MOUNTED)) {
return Environment.getExternalStorageDirectory().toString();
}
return null;
}
在之前提到初始化之前要调用APPID的设置接口,不然导航会没声音,那么下一步就是把刚才APPID的设置的那块代码拿过来用咯。
最后才是真正的初始化操作,先上代码再解释:
/***
*mActivity - 传过来的Activity
*mSDCardPath - 系统根文件夹
*APP_FOLDER_NAME - 应用在SD卡中的目录名
*NaviInitListener- 百度导航初始化监听器
*ttsCallback - 外部TTS能力回调接口,若使用百度内置TTS能力,传入null即可
*ttsHandler - 异步获取百度内部TTS播报状态
*ttsPlayStateListener- 同步获取百度内部TTS播报状态
***/
BaiduNaviManager.getInstance().init(mActivity, mSDCardPath,
APP_FOLDER_NAME, new NaviInitListener() {
@Override
public void initFailed() {
// TODO Auto-generated method stub
System.out.println("失败");
}
@Override
public void initStart() {
// TODO Auto-generated method stub
System.out.println("开始");
}
@Override
public void initSuccess() {
// TODO Auto-generated method stub
Toast.makeText(mActivity, "百度导航引擎初始化成功",
Toast.LENGTH_SHORT).show();
initSetting();
}
@Override
public void onAuthResult(int status, String msg) {
if (0 == status) {
authinfo = "key校验成功!";
} else {
authinfo = "key校验失败, " + msg;
}
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, authinfo,
Toast.LENGTH_LONG).show();
}
});
}
}, null, ttsHandler, ttsPlayStateListener);
导航的初始化监听器能够捕获到四种事件,先校验key,然后开始初始化,再就是初始化的两种结果,失败、成功,这个代码都很易懂,没什么可说的;ttsCallback 这个参数,由于使用的内置TTS,所以我传进了null,ttsHandler以及ttsPlayStateListener,在功能实现上没什么太重要的影响,为了节省时间我给省略了,仅仅做个初步的创建,代码如下:
private Handler ttsHandler = new Handler() {
public void handleMessage(Message msg) {
int type = msg.what;
switch (type) {
case BaiduNaviManager.TTSPlayMsgType.PLAY_START_MSG: {
// showToastMsg("Handler : TTS play start");
break;
}
case BaiduNaviManager.TTSPlayMsgType.PLAY_END_MSG: {
// showToastMsg("Handler : TTS play end");
break;
}
default:
break;
}
}
};
private BaiduNaviManager.TTSPlayStateListener ttsPlayStateListener = new BaiduNaviManager.TTSPlayStateListener() {
@Override
public void playEnd() {
// showToastMsg("TTSPlayStateListener : TTS play end");
}
@Override
public void playStart() {
// showToastMsg("TTSPlayStateListener : TTS play start");
}
};
初始化的操作到此就结束了,初始化成功之后,调一下initSetting(),用来设置导航的部分初始化设置项
private void initSetting() {
BNaviSettingManager.setShowTotalRoadConditionBar(BNaviSettingManager.PreViewRoadCondition.ROAD_CONDITION_BAR_SHOW_ON);
BNaviSettingManager.setVoiceMode(BNaviSettingManager.VoiceMode.Veteran);
BNaviSettingManager
.setRealRoadCondition(BNaviSettingManager.RealRoadCondition.NAVI_ITS_ON);
}
发起算路
通过代码解释吧
/***
*routeplanToNavi - 发起算路
*coType - 坐标系类型
*f_longitude - 当前位置的longitude
*f_latitude - 当前位置的latitude
*fromname - 当前位置名称
***/
public void routeplanToNavi(CoordinateType coType, double f_longitude,
double f_latitude, String fromname) {
// mCoordinateType = coType;
hasInitSuccess=BaiduNaviManager.isNaviInited();//获取是否已经初始化
if (!hasInitSuccess) {
Toast.makeText(mActivity, "还未初始化!", Toast.LENGTH_SHORT).show();
}
// 权限申请
if (android.os.Build.VERSION.SDK_INT >= 23) {
// 保证导航功能完备
if (!hasCompletePhoneAuth()) {
if (!hasRequestComAuth) {
hasRequestComAuth = true;
mActivity
.requestPermissions(authComArr, authComRequestCode);
return;
} else {
Toast.makeText(mActivity, "没有完备的权限!", Toast.LENGTH_SHORT)
.show();
}
}
}
BNRoutePlanNode sNode = null;//起始地点
BNRoutePlanNode eNode = null;//目的地
switch (coType) {
case BD09LL: {
sNode = new BNRoutePlanNode(f_longitude, f_latitude, fromname,
null, coType);
eNode = new BNRoutePlanNode(116.40386525193937, 39.915160800132085, "北京天安门", null,
coType);
break;
}
default:
break;
}
if (sNode != null && eNode != null) {
List<BNRoutePlanNode> list = new ArrayList<BNRoutePlanNode>();
list.add(sNode);
list.add(eNode);
//发起算路操作并在算路成功后通过回调监听器进入导航过程
BaiduNaviManager.getInstance().launchNavigator(mActivity, list, 1,
true, new DemoRoutePlanListener(sNode));
}
}
由于在本次工程中没有用到BD09LL之外的其他坐标系类型,所以,在进行类型判断时我只写了BD09LL的处理方法,在设置目的地的坐标时,为了节省时间,并没有动态设置,而是直接设置了天安门广场的坐标,各位在实际项目中肯定不可能这么干的,终点的坐标直接像当前位置坐标一样通过参数传递过来就行了
BaiduNaviManager.getInstance().launchNavigator(mActivity,list,1,true,new DemoRoutePlanListener(sNode));
launchNavigator的第三个参数“1”,代表的是推荐路线
限定符和类型 | 字段和说明 | 值 |
---|---|---|
static int | ROUTE_PLAN_MOD_AVOID_TAFFICJAM 躲避拥堵 | 16 |
static int | ROUTE_PLAN_MOD_MIN_DIST 少走高速 | 4 |
static int | ROUTE_PLAN_MOD_MIN_TIME 高速优先 | 2 |
static int | ROUTE_PLAN_MOD_MIN_TOLL 少收费 | 8 |
static int | ROUTE_PLAN_MOD_RECOMMEND 推荐 | 1 |
launchNavigator第四个参数true表示真实GPS导航,false表示模拟导航;
DemoRoutePlanListener算路结果监听器,算路成功则可以进入导航界面
public class DemoRoutePlanListener implements RoutePlanListener {
private BNRoutePlanNode mBNRoutePlanNode = null;
public DemoRoutePlanListener(BNRoutePlanNode node) {
mBNRoutePlanNode = node;
}
@Override
public void onJumpToNavigator() {
/*
* 设置途径点以及resetEndNode会回调该接口
*/
for (Activity ac : activityList) {
//判断当前界面是否是诱导view,如果是,就不需要跳转了
if (ac.getClass().getName().endsWith("BNDemoGuideActivity")) {
return;
}
}
Intent intent = new Intent(mActivity, BNDemoGuideActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable(ROUTE_PLAN_NODE,
(BNRoutePlanNode) mBNRoutePlanNode);
intent.putExtras(bundle);
mActivity.startActivity(intent);
}
@Override
public void onRoutePlanFailed() {
// TODO Auto-generated method stub
Toast.makeText(mActivity, "算路失败", Toast.LENGTH_SHORT).show();
}
}
通过监听器代码可以看出,算路成功后,跳转到诱导view,将目的地的坐标通过可序列化对象接口的方式传递给跳转页面,那么,我们应该新建一个诱导view的Activity,自定义Activity名为“BNDemoGuideActivity”,继承于Activity,但是不要xml布局文件,这个在官方文档给的demo中有,直接拷贝过来稍微改改用就可以, 记得在AndroidManifest.xml中注册下哦
<activity
android:name=".BNDemoGuideActivity"
android:configChanges="orientation|screenSize|keyboard|keyboardHidden"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
</activity>
BNDemoGuideActivity
直接上代码
import java.util.ArrayList;
import java.util.List;
import com.baidu.navisdk.adapter.BNRouteGuideManager;
import com.baidu.navisdk.adapter.BNRouteGuideManager.CustomizedLayerItem;
import com.baidu.navisdk.adapter.BNRouteGuideManager.OnNavigationListener;
import com.baidu.navisdk.adapter.BNRoutePlanNode;
import com.baidu.navisdk.adapter.BNaviBaseCallbackModel;
import com.baidu.navisdk.adapter.BaiduNaviCommonModule;
import com.baidu.navisdk.adapter.NaviModuleFactory;
import com.baidu.navisdk.adapter.NaviModuleImpl;
import com.baidu.navisdk.adapter.BNRoutePlanNode.CoordinateType;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class BNDemoGuideActivity extends Activity {
private final String TAG = BNDemoGuideActivity.class.getName();
private BNRoutePlanNode mBNRoutePlanNode = null;
private BaiduNaviCommonModule mBaiduNaviCommonModule = null;
private boolean useCommonInterface = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BaiduNaviOperation.activityList.add(this);
createHandler();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
}
View view = null;
if (useCommonInterface) {
//使用通用接口
mBaiduNaviCommonModule = NaviModuleFactory.getNaviModuleManager().getNaviCommonModule(
NaviModuleImpl.BNaviCommonModuleConstants.ROUTE_GUIDE_MODULE, this,
BNaviBaseCallbackModel.BNaviBaseCallbackConstants.CALLBACK_ROUTEGUIDE_TYPE, mOnNavigationListener);
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onCreate();
view = mBaiduNaviCommonModule.getView();
}
} else {
//使用传统接口
view = BNRouteGuideManager.getInstance().onCreate(this,mOnNavigationListener);
}
if (view != null) {
setContentView(view);
}
Intent intent = getIntent();
if (intent != null) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
mBNRoutePlanNode = (BNRoutePlanNode) bundle.getSerializable(BaiduNaviOperation.ROUTE_PLAN_NODE);
}
}
if (hd != null) {
hd.sendEmptyMessageAtTime(MSG_SHOW, 5000);
}
}
@Override
protected void onResume() {
super.onResume();
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onResume();
}
} else {
BNRouteGuideManager.getInstance().onResume();
}
}
protected void onPause() {
super.onPause();
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onPause();
}
} else {
BNRouteGuideManager.getInstance().onPause();
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onDestroy();
}
} else {
BNRouteGuideManager.getInstance().onDestroy();
}
BaiduNaviOperation.activityList.remove(this);
}
@Override
protected void onStop() {
super.onStop();
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onStop();
}
} else {
BNRouteGuideManager.getInstance().onStop();
}
}
@Override
public void onBackPressed() {//返回键退出导航页
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onBackPressed(false);
}
} else {
BNRouteGuideManager.getInstance().onBackPressed(false);
}
}
public void onConfigurationChanged(android.content.res.Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onConfigurationChanged(newConfig);
}
} else {
BNRouteGuideManager.getInstance().onConfigurationChanged(newConfig);
}
};
@Override
public boolean onKeyDown(int keyCode, android.view.KeyEvent event) {
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
Bundle mBundle = new Bundle();
mBundle.putInt(RouteGuideModuleConstants.KEY_TYPE_KEYCODE, keyCode);
mBundle.putParcelable(RouteGuideModuleConstants.KEY_TYPE_EVENT, event);
mBaiduNaviCommonModule.setModuleParams(RouteGuideModuleConstants.METHOD_TYPE_ON_KEY_DOWN, mBundle);
try {
Boolean ret = (Boolean)mBundle.get(RET_COMMON_MODULE);
if(ret) {
return true;
}
}catch(Exception e){
e.printStackTrace();
}
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onStart() {
super.onStart();
// TODO Auto-generated method stub
if(useCommonInterface) {
if(mBaiduNaviCommonModule != null) {
mBaiduNaviCommonModule.onStart();
}
} else {
BNRouteGuideManager.getInstance().onStart();
}
}
//设置自定义图层
private void addCustomizedLayerItems() {
List<CustomizedLayerItem> items = new ArrayList<CustomizedLayerItem>();
CustomizedLayerItem item1 = null;
if (mBNRoutePlanNode != null) {
item1 = new CustomizedLayerItem(mBNRoutePlanNode.getLongitude(), mBNRoutePlanNode.getLatitude(),
mBNRoutePlanNode.getCoordinateType(), getResources().getDrawable(R.drawable.ic_launcher),
CustomizedLayerItem.ALIGN_CENTER);
items.add(item1);
BNRouteGuideManager.getInstance().setCustomizedLayerItems(items);
}
BNRouteGuideManager.getInstance().showCustomizedLayer(true);
}
private static final int MSG_SHOW = 1;
private static final int MSG_HIDE = 2;
private static final int MSG_RESET_NODE = 3;
private Handler hd = null;
private void createHandler() {
if (hd == null) {
hd = new Handler(getMainLooper()) {
public void handleMessage(android.os.Message msg) {
if (msg.what == MSG_SHOW) {//显示自定义图层
addCustomizedLayerItems();
} else if (msg.what == MSG_HIDE) {//隐藏自定义图层
BNRouteGuideManager.getInstance().showCustomizedLayer(false);
} else if (msg.what == MSG_RESET_NODE) {//重置终点
BNRouteGuideManager.getInstance().resetEndNodeInNavi(
new BNRoutePlanNode(116.21142, 40.85087, " ", null, CoordinateType.GCJ02));//这坐标根据自身情况设置
}
};
};
}
}
private OnNavigationListener mOnNavigationListener = new OnNavigationListener() {
@Override
public void onNaviGuideEnd() {
finish();
}
@Override
public void notifyOtherAction(int actionType, int arg1, int arg2, Object obj) {
if (actionType == 0) {
}
Log.i(TAG, "actionType:" + actionType + "arg1:" + arg1 + "arg2:" + arg2 + "obj:" + obj.toString());
}
};
private final static String RET_COMMON_MODULE = "module.ret";
private interface RouteGuideModuleConstants {
final static int METHOD_TYPE_ON_KEY_DOWN = 0x01;
final static String KEY_TYPE_KEYCODE = "keyCode";
final static String KEY_TYPE_EVENT = "event";
}
}
这一页没什么解释的,有什么疑问留言吧