转载请标明地址:http://blog.csdn.net/gaolei1201/article/details/60876811
2016一路有你,2017一起奋斗!
最近共享单车很火,动辄几亿美刀,屌丝的我只有羡慕的份。啥时候自己也能创一番事业呢?我眉头紧皱深深地思索着。个人认为LBS是移动互联网最主要的特征之一,自己以前没做过地图有关的项目,看到网上也没有完整有关地图的项目,就想起模仿一下摩拜单车app,我这个小项目包括附近车辆、规划路径、行驶距离、行驶轨迹记录、轨迹回放导航等(也挺全的哈);
需要注意:
0、其中的附近车辆用的是假数据,实际项目中你上传自己的经纬度然后服务器端会返回给你附近车辆列表显示出来就行。行驶轨迹记录都是保存在本地数据库,实际项目中你可以隔几秒上传一次踩点列表到服务器,防止APP被杀死或其它异常导致以前踩点消失
1、距离是取两个位置点的直线距离,DistanceUtil.getDistance(lastLatLng, currentLatLng)。然后把所有这些距离相加就是总距离,这是通常算法 2、实际项目中可定时你上传当前位置,然后服务器返回给你附近自行车数据,你展示一下就行。
3、行驶轨迹就是开启后台Service每隔几秒收集一次经纬度,到最后必须把所有经纬度上传到服务器,这样就算app被卸载,重新安装你还可以获取到行驶轨迹。有两种思路,一是边收集变上传到服务器或数据库,这样可以防止手机重启或App被杀死导致以前的数据消失,二是等结束进程时上传到服务器。
4、百度内置导航语音播报的问题:能正常导航,但是无法语音播报?
除了地图显示、定位、导航需要的配置之外,tts播报需要添加白名单,点击进入配置地址。可参考:http://blog.csdn.net/chentravelling/article/details/51435976。
还有就是要分清提交时是debug版和release版的MD5,如果是测试版MD5那么发布版的语音还是没声音
发布版md5或sha获取方法:keytool -list -v -keystore /Users/gaolei/Work/CompanyProject/Bike/BiuBike/BiuBike/biubike.jks
测试版md5或sha获取方法:keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
百度地图开放平台注册不需要公司营业执照什么的,个人就能注册,地址:http://lbsyun.baidu.com。
首先建议大家吧百度地图API的demo下载下来研究一下,它包含我们用到的所有知识点,你再把资源整合一下就行了。SDK的Demo下载地址:http://lbsyun.baidu.com/sdk/download?selected=mapsdk_basicmap,mapsdk_searchfunction,mapsdk_lbscloudsearch,mapsdk_calculationtool,mapsdk_radar
运行效果图
下面简单介绍一下有关内容,有需要的可以下载源码运行研究
1、初始化
SDKInitializer.initialize(getApplicationContext());//我测试在Application的onCreate()不行,必须在activity的onCreate()中
2、配置map参数
private void initMap() { // 地图初始化 mMapView = (MapView) findViewById(R.id.id_bmapView); mBaiduMap = mMapView.getMap(); // 开启定位图层 mBaiduMap.setMyLocationEnabled(true); // 定位初始化 mlocationClient = new LocationClient(this); mlocationClient.registerLocationListener(myListener); LocationClientOption option = new LocationClientOption(); option.setOpenGps(true); // 打开gps option.setCoorType("bd09ll"); // 设置坐标类型 option.setScanSpan(5000);//设置onReceiveLocation()获取位置的频率 option.setIsNeedAddress(true);//如想获得具体位置就需要设置为true mlocationClient.setLocOption(option); mlocationClient.start(); mCurrentMode = MyLocationConfiguration.LocationMode.FOLLOWING; mBaiduMap.setMyLocationConfigeration(new MyLocationConfiguration( mCurrentMode, true, null)); myOrientationListener = new MyOrientationListener(this); //通过接口回调来实现实时方向的改变 myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() { @Override public void onOrientationChanged(float x) { mCurrentX = x; } }); myOrientationListener.start(); mSearch = RoutePlanSearch.newInstance(); mSearch.setOnGetRoutePlanResultListener(this); initMarkerClickEvent(); }
3、获取当前地址
public class MyLocationListenner implements BDLocationListener { @Override public void onReceiveLocation(BDLocation bdLocation) { // map view 销毁后不在处理新接收的位置 if (bdLocation == null || mMapView == null) { return; } MyLocationData locData = new MyLocationData.Builder() .accuracy(bdLocation.getRadius()) .direction(mCurrentX)//设定图标方向 // 此处设置开发者获取到的方向信息,顺时针0-360 .latitude(bdLocation.getLatitude()) .longitude(bdLocation.getLongitude()).build(); mBaiduMap.setMyLocationData(locData); currentLatitude = bdLocation.getLatitude(); currentLongitude = bdLocation.getLongitude(); current_addr.setText(bdLocation.getAddrStr()); currentLL = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude()); startNodeStr = PlanNode.withLocation(currentLL); //option.setScanSpan(5000),每隔5000ms这个方法就会调用一次,而有些我们只想调用一次,所以要判断一下isFirstLoc if (isFirstLoc) { isFirstLoc = false; LatLng ll = new LatLng(bdLocation.getLatitude(), bdLocation.getLongitude()); MapStatus.Builder builder = new MapStatus.Builder(); //地图缩放比设置为18 builder.target(ll).zoom(18.0f); mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build())); changeLatitude = bdLocation.getLatitude(); changeLongitude = bdLocation.getLongitude(); if (!isServiceLive) { addOverLayout(currentLatitude, currentLongitude); } } } }
4、开启service来每隔几秒收集一次经纬度信息,保存到列表,然后通过broadcast把数据传到MainActivity来更新时间和距离UI信息
public class RouteService extends Service { private double currentLatitude, currentLongitude; private LocationClient mlocationClient = null; private MylocationListener mlistener; private BitmapDescriptor mIconLocation; private MyOrientationListener myOrientationListener; private float mCurrentX; //定位图层显示方式 private MyLocationConfiguration.LocationMode locationMode; AllInterface.IUpdateLocation iUpdateLocation; public ArrayList<RoutePoint> routPointList = new ArrayList<RoutePoint>(); public int totalDistance = 0; public float totalPrice = 0; public long beginTime = 0, totalTime = 0; Notification notification; RemoteViews contentView; public void setiUpdateLocation(AllInterface.IUpdateLocation iUpdateLocation) { this.iUpdateLocation = iUpdateLocation; } public void onCreate() { Log.d("gaolei", "RouteService--------onCreate-------------"); super.onCreate(); beginTime = System.currentTimeMillis(); // RouteDBHelper dbHelper = new RouteDBHelper(this); // // 只有调用了DatabaseHelper的getWritableDatabase()方法或者getReadableDatabase()方法之后,才会创建或打开一个连接 // SQLiteDatabase sqliteDatabase = dbHelper.getReadableDatabase(); totalTime = 0; totalDistance = 0; totalPrice = 0; routPointList.clear(); } public int onStartCommand(Intent intent, int flags, int startId) { Log.d("gaolei", "RouteService--------onStartCommand---------------"); initLocation();//初始化LocationgClient initNotification(); Utils.acquireWakeLock(this); // 开启轨迹记录线程 return super.onStartCommand(intent, flags, startId); } private void initNotification() { int icon = R.mipmap.bike_icon2; contentView = new RemoteViews(getPackageName(), R.layout.notification_layout); notification = new NotificationCompat.Builder(this).setContent(contentView).setSmallIcon(icon).build(); Intent notificationIntent = new Intent(this, MainActivity.class); notificationIntent.putExtra("flag", "notification"); notification.contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); } private void initLocation() { mIconLocation = BitmapDescriptorFactory .fromResource(R.mipmap.location_marker); locationMode = MyLocationConfiguration.LocationMode.NORMAL; //定位服务的客户端。宿主程序在客户端声明此类,并调用,目前只支持在主线程中启动 mlocationClient = new LocationClient(this); mlistener = new MylocationListener(); // initMarkerClickEvent(); //注册监听器 mlocationClient.registerLocationListener(mlistener); //配置定位SDK各配置参数,比如定位模式、定位时间间隔、坐标系类型等 LocationClientOption mOption = new LocationClientOption(); //设置坐标类型 mOption.setCoorType("bd09ll"); //设置是否需要地址信息,默认为无地址 mOption.setIsNeedAddress(true); //设置是否打开gps进行定位 mOption.setOpenGps(true); //设置扫描间隔,单位是毫秒 当<1000(1s)时,定时定位无效 int span = 10000; mOption.setScanSpan(span); //设置 LocationClientOption mlocationClient.setLocOption(mOption); //初始化图标,BitmapDescriptorFactory是bitmap 描述信息工厂类. mIconLocation = BitmapDescriptorFactory .fromResource(R.mipmap.location_marker); myOrientationListener = new MyOrientationListener(this); //通过接口回调来实现实时方向的改变 myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() { @Override public void onOrientationChanged(float x) { mCurrentX = x; } }); // mSearch = RoutePlanSearch.newInstance(); // mSearch.setOnGetRoutePlanResultListener(this); // //开启定位 // mBaiduMap.setMyLocationEnabled(true); if (!mlocationClient.isStarted()) { mlocationClient.start(); } myOrientationListener.start(); } private void startNotifi(String time, String distance, String price) { startForeground(1, notification); contentView.setTextViewText(R.id.bike_time, time); contentView.setTextViewText(R.id.bike_distance, distance); contentView.setTextViewText(R.id.bike_price, price); } public IBinder onBind(Intent intent) { Log.d("gaolei", "onBind-------------"); return null; } public boolean onUnBind(Intent intent) { Log.d("gaolei", "onBind-------------"); return false; } @Override public void onDestroy() { super.onDestroy(); mlocationClient.stop(); myOrientationListener.stop(); Log.d("gaolei", "RouteService----0nDestroy---------------"); Gson gson = new Gson(); String routeListStr = gson.toJson(routPointList); Log.d("gaolei", "RouteService----routeListStr-------------" + routeListStr); Bundle bundle = new Bundle(); bundle.putString("totalTime", totalTime + ""); bundle.putString("totalDistance", totalDistance + ""); bundle.putString("totalPrice", totalPrice + ""); bundle.putString("routePoints", routeListStr); Intent intent = new Intent(this, RouteDetailActivity.class); intent.putExtras(bundle); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); if (routPointList.size() > 2) insertData(routeListStr); Utils.releaseWakeLock(); stopForeground(true); } //所有的定位信息都通过接口回调来实现 public class MylocationListener implements BDLocationListener { //定位请求回调接口 private boolean isFirstIn = true; //定位请求回调函数,这里面会得到定位信息 @Override public void onReceiveLocation(BDLocation bdLocation) { if (null == bdLocation) return; //"4.9E-324"表示目前所处的环境(室内或者是网络状况不佳)造成无法获取到经纬度 if ("4.9E-324".equals(String.valueOf(bdLocation.getLatitude())) || "4.9E-324".equals(String.valueOf(bdLocation.getLongitude()))) { return; }//过滤百度定位失败 Log.d("gaolei", "RouteService---------getAddrStr()-------------" + bdLocation.getAddrStr()); double routeLat = bdLocation.getLatitude(); double routeLng = bdLocation.getLongitude(); RoutePoint routePoint = new RoutePoint(); routePoint.setRouteLat(routeLat); routePoint.setRouteLng(routeLng); if (routPointList.size() == 0) routPointList.add(routePoint); else { RoutePoint lastPoint = routPointList.get(routPointList.size() - 1); if (routeLat == lastPoint.getRouteLat() && routeLng == lastPoint.getRouteLng()) { } else { LatLng lastLatLng = new LatLng(lastPoint.getRouteLat(), lastPoint.getRouteLng()); LatLng currentLatLng = new LatLng(routeLat, routeLng); if (routeLat > 0 && routeLng > 0) {//经纬度都不能为0 double distantce = DistanceUtil.getDistance(lastLatLng, currentLatLng); // 大于5米才加入列表 if (distantce > 5) { routPointList.add(routePoint); totalDistance += distantce; } } } } totalTime = (int) (System.currentTimeMillis() - beginTime) / 1000 / 60; totalPrice = (float) (Math.floor(totalTime / 30) * 0.5 + 0.5); // Log.d("gaolei", "biginTime--------------" + beginTime); Log.d("gaolei", "totalTime--------------" + totalTime); Log.d("gaolei", "totalDistance--------------" + totalDistance); startNotifi(totalTime + "分钟", totalDistance + "米", totalPrice + "元"); Intent intent = new Intent("com.locationreceiver"); Bundle bundle = new Bundle(); bundle.putString("totalTime", totalTime + "分钟"); bundle.putString("totalDistance", totalDistance + "米"); bundle.putString("totalPrice", totalPrice + "元"); intent.putExtras(bundle); sendBroadcast(intent); } } public static class NetWorkReceiver extends BroadcastReceiver { public NetWorkReceiver() { } @Override public void onReceive(Context context, Intent intent) { NetworkInfo.State wifiState = null; NetworkInfo.State mobileState = null; ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); wifiState = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState(); mobileState = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState(); if (wifiState != null && mobileState != null && NetworkInfo.State.CONNECTED != wifiState && NetworkInfo.State.CONNECTED == mobileState) { // Toast.makeText(context, context.getString(R.string.net_mobile), Toast.LENGTH_SHORT).show(); // 手机网络连接成功 } else if (wifiState != null && mobileState != null && NetworkInfo.State.CONNECTED != wifiState && NetworkInfo.State.CONNECTED != mobileState) { // Toast.makeText(context, context.getString(R.string.net_none), Toast.LENGTH_SHORT).show(); // 手机没有任何的网络 } else if (wifiState != null && NetworkInfo.State.CONNECTED == wifiState) { // 无线网络连接成功 // Toast.makeText(context, context.getString(R.string.net_wifi), Toast.LENGTH_SHORT).show(); } } } public void insertData(String routeListStr) { ContentValues values = new ContentValues(); // 向该对象中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据当中的数据类型一致 values.put("cycle_date", Utils.getDateFromMillisecond(beginTime)); values.put("cycle_time", totalTime); values.put("cycle_distance", totalDistance); values.put("cycle_price", totalPrice); values.put("cycle_points", routeListStr); // 创建DatabaseHelper对象 RouteDBHelper dbHelper = new RouteDBHelper(this); // 得到一个可写的SQLiteDatabase对象 SQLiteDatabase sqliteDatabase = dbHelper.getWritableDatabase(); // 调用insert方法,就可以将数据插入到数据库当中 // 第一个参数:表名称 // 第二个参数:SQl不允许一个空列,如果ContentValues是空的,那么这一列被明确的指明为NULL值 // 第三个参数:ContentValues对象 sqliteDatabase.insert("cycle_route", null, values); sqliteDatabase.close(); } }
5、结束行程,可以查看行驶轨迹
public class RouteDetailActivity extends BaseActivity { private MapView route_detail_mapview; BaiduMap routeBaiduMap; private BitmapDescriptor startBmp, endBmp; private MylocationListener mlistener; LocationClient mlocationClient; TextView total_time, total_distance, total_price; public ArrayList<RoutePoint> routePoints; public static boolean completeRoute = false; String time, distance, price, routePointsStr; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_route_detail); setStatusBar(); route_detail_mapview = (MapView) findViewById(R.id.route_detail_mapview); total_time = (TextView) findViewById(R.id.total_time); total_distance = (TextView) findViewById(R.id.total_distance); total_price = (TextView) findViewById(R.id.total_pricce); routeBaiduMap = route_detail_mapview.getMap(); route_detail_mapview.showZoomControls(false); startBmp = BitmapDescriptorFactory.fromResource(R.mipmap.route_start); endBmp = BitmapDescriptorFactory.fromResource(R.mipmap.route_end); initMap(); Intent intent = getIntent(); String time = intent.getStringExtra("totalTime"); String distance = intent.getStringExtra("totalDistance"); String price = intent.getStringExtra("totalPrice"); routePointsStr = intent.getStringExtra("routePoints"); routePoints = new Gson().fromJson(routePointsStr, new TypeToken<List<RoutePoint>>() { }.getType()); List<LatLng> points = new ArrayList<LatLng>(); for (int i = 0; i < routePoints.size(); i++) { RoutePoint point = routePoints.get(i); LatLng latLng = new LatLng(point.getRouteLat(), point.getRouteLng()); Log.d("gaolei", "point.getRouteLat()----show-----" + point.getRouteLat()); Log.d("gaolei", "point.getRouteLng()----show-----" + point.getRouteLng()); points.add(latLng); } if (points.size() > 2) { OverlayOptions ooPolyline = new PolylineOptions().width(10) .color(0xFF36D19D).points(points); routeBaiduMap.addOverlay(ooPolyline); RoutePoint startPoint = routePoints.get(0); LatLng startPosition = new LatLng(startPoint.getRouteLat(), startPoint.getRouteLng()); MapStatus.Builder builder = new MapStatus.Builder(); builder.target(startPosition).zoom(18.0f); routeBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build())); RoutePoint endPoint = routePoints.get(routePoints.size() - 1); LatLng endPosition = new LatLng(endPoint.getRouteLat(), endPoint.getRouteLng()); addOverLayout(startPosition, endPosition); } total_time.setText("骑行时长:" + time + "分钟"); total_distance.setText("骑行距离:" + distance + "米"); total_price.setText("余额支付:" + price + "元"); } private void initMap() { mlocationClient = new LocationClient(this); // mlistener = new MylocationListener(); // mlocationClient.registerLocationListener(mlistener); LocationClientOption mOption = new LocationClientOption(); //设置坐标类型 mOption.setCoorType("bd09ll"); //设置是否需要地址信息,默认为无地址 mOption.setIsNeedAddress(true); //设置是否打开gps进行定位 mOption.setOpenGps(true); //设置扫描间隔,单位是毫秒 当<1000(1s)时,定时定位无效 int span = 10000; mOption.setScanSpan(span); //设置 LocationClientOption mlocationClient.setLocOption(mOption); if (!mlocationClient.isStarted()) { mlocationClient.start(); } UiSettings settings=routeBaiduMap.getUiSettings(); settings.setScrollGesturesEnabled(true); } public class MylocationListener implements BDLocationListener { //定位请求回调接口 private boolean isFirstIn = true; //定位请求回调函数,这里面会得到定位信息 @Override public void onReceiveLocation(BDLocation bdLocation) { //判断是否为第一次定位,是的话需要定位到用户当前位置 if (isFirstIn) { Log.d("gaolei", "onReceiveLocation----------RouteDetail-----" + bdLocation.getAddrStr()); // LatLng currentLL = new LatLng(bdLocation.getLatitude(), // bdLocation.getLongitude()); startNodeStr = PlanNode.withLocation(currentLL); // MapStatus.Builder builder = new MapStatus.Builder(); // builder.target(currentLL).zoom(18.0f); // routeBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build())); isFirstIn = false; } } } private void addOverLayout(LatLng startPosition, LatLng endPosition) { //先清除图层 // mBaiduMap.clear(); // 定义Maker坐标点 // 构建MarkerOption,用于在地图上添加Marker MarkerOptions options = new MarkerOptions().position(startPosition) .icon(startBmp); // 在地图上添加Marker,并显示 routeBaiduMap.addOverlay(options); MarkerOptions options2 = new MarkerOptions().position(endPosition) .icon(endBmp); // 在地图上添加Marker,并显示 routeBaiduMap.addOverlay(options2); } public void onDestroy() { super.onDestroy(); routeBaiduMap.setMyLocationEnabled(false); mlocationClient.stop(); completeRoute = false; } public void finishActivity(View view) { completeRoute = true; finish(); } public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { completeRoute = true; finish(); return true; } return super.onKeyDown(keyCode, event); } }
导航类:
/** * Created by GaoLei on 17/3/31. * 这个工具类实现了调用内置导航和打开第三方App导航 * 1.assets中的文件必须拷贝到项目 * 2.想使用内置导航,必须初始化导航, NavUtil.initNavi(this); */ public class NavUtil { public static final int BaiduNavi = 1, GaodeNavi = 2, InnerNavi = 0; public static List<Activity> activityList = new LinkedList<Activity>(); public static final String ROUTE_PLAN_NODE = "routePlanNode"; static String authinfo = null; /** * 弹出导航选择dialog */ public static void showChoiceNaviWayDialog(final Activity activity, final LatLng startLL, final LatLng endLL, final String start_place, final String destination) { final NaviSelectDialog rcd = new NaviSelectDialog(activity); rcd.setCanceledOnTouchOutside(false); rcd.setCancelable(false); final ArrayList<String> mapApps = new ArrayList<String>(); mapApps.add(activity.getString(R.string.inner_navi)); if (Utils.hasApp(activity, Utils.APP_BAIDU_MAP)) { mapApps.add(activity.getString(R.string.baidu_navi)); } if (Utils.hasApp(activity, Utils.APP_AMAP)) { mapApps.add(activity.getString(R.string.gaode_navi)); } rcd.setItems(mapApps, new NaviSelectDialog.OnDlgItemClickListener() { @Override public void onEnsureClicked(Dialog dialog, String value, boolean isChecked) { dialog.dismiss(); if (activity.getString(R.string.inner_navi).equals(value)) { launchNavigatorViaPoints(activity, startLL, endLL); // startInnerNavi(activity, startLL, endLL); } if (activity.getString(R.string.baidu_navi).equals(value)) { // startNative_Baidu(activity, startLL, endLL, start_place, destination); startBikingNavi(activity, startLL, endLL); } else if (activity.getString(R.string.gaode_navi).equals(value)) { startGaodeNavi(activity, startLL, endLL, start_place); } if (isChecked) { //记住我的选择 } } public void onCancleClicked(Dialog dialog) { dialog.dismiss(); } }, true).show(); } private static void launchNavigatorViaPoints(final Activity activity, LatLng startLL, LatLng endLL) { //这里给出一个起终点示例,实际应用中可以通过POI检索、外部POI来源等方式获取起终点坐标 activityList.add(activity); final BNRoutePlanNode sNode = new BNRoutePlanNode(startLL.longitude, startLL.latitude, null, "从这里开始", BNRoutePlanNode.CoordinateType.BD09LL); final BNRoutePlanNode eNode = new BNRoutePlanNode(endLL.longitude, endLL.latitude, null, "到这里结束", BNRoutePlanNode.CoordinateType.BD09LL); if (sNode != null && eNode != null) { List<BNRoutePlanNode> points = new ArrayList<BNRoutePlanNode>(); points.add(sNode); points.add(eNode); //距离太近toast提示(100米内) double dis = DistanceUtil.getDistance(new LatLng(sNode.getLatitude(), sNode.getLongitude()), new LatLng(eNode.getLatitude(), eNode.getLongitude())); if (dis <= 100) { Toast.makeText(activity, "起点、途经点、终点距离太近", Toast.LENGTH_SHORT).show(); return; } BaiduNaviManager.getInstance().launchNavigator(activity, points, 1, true, new BaiduNaviManager.RoutePlanListener() { public void onJumpToNavigator() { /* * 设置途径点以及resetEndNode会回调该接口 */ for (Activity ac : activityList) { if (ac.getClass().getName().endsWith("BNDemoGuideActivity")) { return; } } Intent intent = new Intent(activity, BDInnerNaviActivity.class); Bundle bundle = new Bundle(); bundle.putSerializable(ROUTE_PLAN_NODE, (BNRoutePlanNode) sNode); intent.putExtras(bundle); activity.startActivity(intent); } public void onRoutePlanFailed() { // TODO Auto-generated method stub Toast.makeText(activity, "算路失败", Toast.LENGTH_SHORT).show(); } }); } } /** * 启动百度地图骑行导航(Native) */ private static void startBikingNavi(Activity activity, LatLng startLL, LatLng endLL) { //距离太近toast提示(100米内) double dis = DistanceUtil.getDistance(new LatLng(startLL.latitude, startLL.longitude), new LatLng(endLL.latitude, endLL.longitude)); if (dis <= 100) { Toast.makeText(activity, "起点、途经点、终点距离太近", Toast.LENGTH_SHORT).show(); return; } // 构建 导航参数 NaviParaOption para = new NaviParaOption() .startPoint(startLL).endPoint(endLL); try { BaiduMapNavigation.openBaiduMapBikeNavi(para, activity); } catch (BaiduMapAppNotSupportNaviException e) { e.printStackTrace(); } } /** * 启动百度地图导航(Native) */ public void startNavi(Activity activity, LatLng pt1, LatLng pt2) { // 构建 导航参数 NaviParaOption para = new NaviParaOption() .startPoint(pt1).endPoint(pt2) .startName("天安门").endName("百度大厦"); try { BaiduMapNavigation.openBaiduMapNavi(para, activity); } catch (BaiduMapAppNotSupportNaviException e) { e.printStackTrace(); showDialog(activity); } } /** * 启动百度地图驾车路线规划 */ public void startRoutePlanDriving(Activity activity, LatLng pt1, LatLng pt2) { // 构建 route搜索参数 RouteParaOption para = new RouteParaOption() .startPoint(pt1) .endPoint(pt2); try { BaiduMapRoutePlan.openBaiduMapDrivingRoute(para, activity); } catch (Exception e) { e.printStackTrace(); showDialog(activity); } } /** * 通过Uri跳转到百度地图导航 */ public static void startNative_Baidu(Activity activity, LatLng pt1, LatLng pt2, String start_address, String end_address) { try { double dis = DistanceUtil.getDistance(new LatLng(pt1.latitude,pt1.longitude), new LatLng(pt2.latitude,pt2.longitude)); if (dis <= 100) { Toast.makeText(activity, "起点、途经点、终点距离太近", Toast.LENGTH_SHORT).show(); return; } String start_latlng = pt1.latitude + "," + pt1.longitude; String end_latlng = pt2.latitude + "," + pt2.longitude; Intent intent = Intent.getIntent("intent://map/direction?origin=latlng:"+start_latlng+"|name:"+"Start"+"&destination=latlng:"+end_latlng+"|name:"+"End"+"&mode=riding&src=这里随便写#Intent;scheme=bdapp;package=com.baidu.BaiduMap;end"); Log.d("gaolei", "---------------" + start_address + "," + end_address); activity.startActivity(intent); } catch (Exception e) { e.printStackTrace(); Toast.makeText(activity, "地址解析错误", Toast.LENGTH_SHORT).show(); } } /** * 启动高德地图驾车路线规划 */ public static void startGaodeNavi(Activity activity, LatLng pt1, LatLng pt2, String start_place) { try { Intent intent = new Intent(); double sLat = pt1.latitude, sLon = pt1.longitude, eLat = pt2.latitude, eLon = pt2.longitude; String poiAddress = LocationManager.getInstance().getAddress(); Log.d("gaolei", "poiAddress---------gaode-----------" + poiAddress); intent.setData(android.net.Uri .parse("androidamap://navi?sourceApplication=yongche&poiname=" + start_place + "&lat=" + eLat + "&lon=" + eLon + "&dev=0&style=2")); intent.addCategory("android.intent.category.DEFAULT"); intent.setPackage("com.autonavi.minimap"); activity.startActivity(intent); } catch (Exception e) { e.printStackTrace(); } } /** * 路线规划监听器,规划成功后跳转至导航过程页面 */ private static class YCRoutePlanListener implements BaiduNaviManager.RoutePlanListener { private BNRoutePlanNode mBNRoutePlanNode = null; private Activity activity; public YCRoutePlanListener(BNRoutePlanNode node, Activity act) { mBNRoutePlanNode = node; activity = act; } @Override public void onJumpToNavigator() { Intent intent = new Intent(activity, BDInnerNaviActivity.class); activity.startActivity(intent); activity.startActivity(intent); } @Override public void onRoutePlanFailed() { } } /** * 提示未安装百度地图app或app版本过低 */ public void showDialog(final Activity activity) { AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setMessage("您尚未安装百度地图app或app版本过低,点击确认安装?"); builder.setTitle("提示"); builder.setPositiveButton("确认", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); OpenClientUtil.getLatestBaiduMapApp(activity); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.create().show(); } public static void initNavi(final Activity activity) { BaiduNaviManager.getInstance().init(activity, mSDCardPath, APP_FOLDER_NAME, new BaiduNaviManager.NaviInitListener() { public void onAuthResult(int status, String msg) { if (0 == status) { // authinfo = "key校验成功!"; } else { // authinfo = "key校验失败, " + msg; } activity.runOnUiThread(new Runnable() { @Override public void run() { // Toast.makeText(activity, authinfo, Toast.LENGTH_LONG).show(); } }); } public void initSuccess() { // Toast.makeText(activity, "百度导航引擎初始化成功", Toast.LENGTH_SHORT).show(); initSetting(); } public void initStart() { // Toast.makeText(activity, "百度导航引擎初始化开始", Toast.LENGTH_SHORT).show(); } public void initFailed() { // Toast.makeText(activity, "百度导航引擎初始化失败", Toast.LENGTH_SHORT).show(); } }, null, ttsHandler, ttsPlayStateListener); } private static void initSetting() { // 设置是否双屏显示 BNaviSettingManager.setShowTotalRoadConditionBar(BNaviSettingManager.PreViewRoadCondition.ROAD_CONDITION_BAR_SHOW_ON); // 设置导航播报模式 BNaviSettingManager.setVoiceMode(BNaviSettingManager.VoiceMode.Veteran); // 是否开启路况 BNaviSettingManager.setRealRoadCondition(BNaviSettingManager.RealRoadCondition.NAVI_ITS_ON); } /** * 内部TTS播报状态回传handler */ private static 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; } } }; /** * 内部TTS播报状态回调接口 */ private static BaiduNaviManager.TTSPlayStateListener ttsPlayStateListener = new BaiduNaviManager.TTSPlayStateListener() { @Override public void playEnd() { // showToastMsg("TTSPlayStateListener : TTS play end"); } @Override public void playStart() { // showToastMsg("TTSPlayStateListener : TTS play start"); } }; }
有关轨迹回放的原理是:截取所有记录point的一部分逐渐增加,然后显示,主要是通过handler,每隔1S增加截取一次,
Handler handler = new Handler() { public void handleMessage(Message msg) { currentIndex = currentIndex + spanIndex; Log.d("gaolei", "currentIndex------------" + currentIndex); routeBaiduMap.clear(); subList = points.subList(0, currentIndex); if (subList.size() >= 2) { OverlayOptions ooPolyline = new PolylineOptions().width(10) .color(0xFF36D19D).points(subList); routeBaiduMap.addOverlay(ooPolyline); } if (subList.size() >= 1) { LatLng latLng = points.get(subList.size() - 1); MarkerOptions options = new MarkerOptions().position(latLng) .icon(currentBmp); // 在地图上添加Marker,并显示 routeBaiduMap.addOverlay(options); MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng); // 移动到某经纬度 routeBaiduMap.animateMapStatus(update); } if (currentIndex < routePointsLength) { tv_current_time.setText(Utils.getDateFromMillisecond(routePoints.get(currentIndex).time)); tv_current_speed.setText(routePoints.get(currentIndex).speed + "km/h"); int progress = (int) currentIndex * 100 / routePointsLength; seekbar_progress.setProgress(progress); handler.sendEmptyMessageDelayed(1, 1000); } else { OverlayOptions ooPolyline = new PolylineOptions().width(10) .color(0xFF36D19D).points(points); routeBaiduMap.addOverlay(ooPolyline); seekbar_progress.setProgress(100); handler.removeCallbacksAndMessages(null); Toast.makeText(RouteDetailActivity.this, "轨迹回放结束", Toast.LENGTH_LONG).show(); } } };
运行效果:
------------------------------------------------------
遇到的大坑:
1、我的 百度 key肯定是没错,在魅族4.4系统上正常,而在三星C7 和coopad 106(都是6.0系统)不能正常显示,而是一片蓝,获取不到当前位置?如图
解决方法:原因是我设置targetVersion>=23,那么运行到6.0及以上设备时,默认所有权限都不开启,必须动态requestPermission,这里需要位置权限,默认没开启导致此结果,把targetVersion=22就行,当targetVersion<23时,默认开启全部权限。
1.2、地图只显示网格不显示建筑
解决方法:百度开放平台时 debug md5没配置正确,不同电脑或环境 debug md5 不一样,如果用我的项目直接运行可能就会出现这种情况,因为你的电脑环境debug md5和我的电脑环境debug md5不一样。你可以打一个release包 因为release md5如果用同一个签名是不会变的。你也可以在百度地图开放平台自己注册账号配置信息
2、仿DrawerLayout的覆盖型侧滑菜单,研究了好久,可参考:http://blog.csdn.net/gaolei1201/article/details/50404941,https://github.com/gaoleiandroid1201/DrawerLayout
3、位置图标不跟着自己运动,查看自己代码也没有问题啊,调试了好久,跑了不少冤枉路,莫名其妙
解决方法:把百度sdk中的初始化地图和配置参数等代码拷进来代替,果然奏效
4、进程保活,这个不能完全保活,只能尽量保活,除非是被加入白名单或自启应用像微信,但是有些 app像行者和咕咚就算app被杀死了也能活下来,有关进程保活可参考:https://mp.weixin.qq.com/s/d3scy-dC46NW9sz7wc3YLQ,https://segmentfault.com/a/1190000006251859
5、如果没有第三方导航APP,可以做百度地图内置导航,也耗费了不少时间。主要是官方Demo不能跑通,自带key验证不通过,还要自己研究
6、如果TTS语音导航SDK注册白名单注册不成功一直提示“registed Already”,可参考:http://bbs.lbsyun.baidu.com/forum.php?mod=viewthread&tid=90839
app体验下载地址:https://github.com/gaoleicoding/BiuBike/raw/master/BiuBike/Biubike_V2.1.1(211)_release.apk