转载请标明地址: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); // 打开gpsoption.setCoorType("bd09ll"); // 设置坐标类型option.setScanSpan(5000);//设置onReceiveLocation()获取位置的频率option.setIsNeedAddress(true);//如想获得具体位置就需要设置为truemlocationClient.setLocOption(option);mlocationClient.start();mCurrentMode = MyLocationConfiguration.LocationMode.FOLLOWING;mBaiduMap.setMyLocationConfigeration(new MyLocationConfiguration(mCurrentMode, true, null));myOrientationListener = new MyOrientationListener(this);//通过接口回调来实现实时方向的改变myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {@Overridepublic void onOrientationChanged(float x) {mCurrentX = x;}});myOrientationListener.start();mSearch = RoutePlanSearch.newInstance();mSearch.setOnGetRoutePlanResultListener(this);initMarkerClickEvent();}

3、获取当前地址

public class MyLocationListenner implements BDLocationListener {@Overridepublic 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这个方法就会调用一次,而有些我们只想调用一次,所以要判断一下isFirstLocif (isFirstLoc) {isFirstLoc = false;LatLng ll = new LatLng(bdLocation.getLatitude(),bdLocation.getLongitude());MapStatus.Builder builder = new MapStatus.Builder();//地图缩放比设置为18builder.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();//初始化LocationgClientinitNotification();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);//设置 LocationClientOptionmlocationClient.setLocOption(mOption);//初始化图标,BitmapDescriptorFactory是bitmap 描述信息工厂类.mIconLocation = BitmapDescriptorFactory.fromResource(R.mipmap.location_marker);myOrientationListener = new MyOrientationListener(this);//通过接口回调来实现实时方向的改变myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {@Overridepublic 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;}@Overridepublic 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;//定位请求回调函数,这里面会得到定位信息@Overridepublic 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) {//经纬度都不能为0double 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() {}@Overridepublic 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);//设置 LocationClientOptionmlocationClient.setLocOption(mOption);if (!mlocationClient.isStarted()) {mlocationClient.start();}UiSettings settings=routeBaiduMap.getUiSettings();settings.setScrollGesturesEnabled(true);}public class MylocationListener implements BDLocationListener {//定位请求回调接口private boolean isFirstIn = true;//定位请求回调函数,这里面会得到定位信息@Overridepublic 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,用于在地图上添加MarkerMarkerOptions 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() {@Overridepublic 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 stubToast.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;}@Overridepublic void onJumpToNavigator() {Intent intent = new Intent(activity, BDInnerNaviActivity.class);activity.startActivity(intent);activity.startActivity(intent);}@Overridepublic 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() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();OpenClientUtil.getLatestBaiduMapApp(activity);}});builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void playEnd() {
//            showToastMsg("TTSPlayStateListener : TTS play end");}@Overridepublic 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

Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,轨迹回放,导航等)相关推荐

  1. Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,导航等)

    Android百度地图实例详解之仿摩拜单车APP(包括附近车辆.规划路径.行驶距离.行驶轨迹记录,导航等) 标签: android百度地图行驶轨迹记录共享单车行驶距离和时间 2017-03-08 20 ...

  2. 仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,导航等)

    本文是由奇虎360公司高磊关于使用百度地图仿摩拜单车APP,原文地址:http://blog.csdn.net/gaolei1201/article/details/60876811 最近共享单车很火 ...

  3. android 滑动接听源码,android仿摩拜单车APP、炫酷RecyclerView、卡片滑动、仿饿了么点餐、自定义索引等源码...

    Android精选源码 Android优质博客 前言permissions4m 最初的设计是仅仅做成一个编译器注解框架,在1.0.0版本时,它纯粹地实现了原生 Android 请求流程,关于它的设计思 ...

  4. android 卡片上滑放大,android仿摩拜单车APP、炫酷RecyclerView、卡片滑动、仿饿了么点餐、自定义索引等源码...

    Android精选源码 Android优质博客 前言permissions4m 最初的设计是仅仅做成一个编译器注解框架,在1.0.0版本时,它纯粹地实现了原生 Android 请求流程,关于它的设计思 ...

  5. Android NFC卡实例详解

    Android NFC卡实例详解 公司最近在做一个NFC卡片的工程,经过几天的时间,终于写了一个Demo出来,在此记录下在此过程中遇到的问题.由于之前本人是做iOS的,Android写起来并不是那么的 ...

  6. 微信小程序仿摩拜单车

    本小程序仿摩拜单车的地图显示和拖动部分,单车数据采用周边厕所模拟.index.wxml如下: <map id="map" bindcontroltap="bindc ...

  7. Android 吸入动画效果详解(仿mac退出效果)

    转载自:http://m.blog.csdn.net/blog/leehong2005/9127095 [转]Android 吸入动画效果详解 1,背景 吸入(Inhale)效果,最初我是在iOS上面 ...

  8. android 百度地图驾车导航,百度地图API详解之驾车导航

    本文将向大家介绍如何使用百度地图API提供的驾车导航服务进行开发. 一个简单的示例 驾车导航服务根据传入的起点和终点信息给出从起点到终点的驾车路线,我们先从一个最简单的示例看起: var map = ...

  9. 百度地图API详解之公交导航

    原文地址:http://blog.csdn.net/sup_heaven/article/details/8461593 只是作为备忘!!! 一次调试百度地图多marker事件监听的问题,不知如何解决 ...

最新文章

  1. 开工快乐!AI内参邀你继续学习,并送上50元优惠券!
  2. 【性能优化实战】java嵌入式开发pos
  3. Java成员变量和局部变量的区别
  4. JasperReports:棘手的部分
  5. 【2020】六家云厂商价格比较:AWS、阿里云、Azure、Google Cloud、华为云、腾讯云
  6. 啥?!BM25 比语义向量检索效果好?
  7. C# 9.0 新功能一览!
  8. java并发编程(2)--线程 原子性 volatile AtomicInteger
  9. 记录一次日志实现,基于log4j2
  10. 分布式微服框架Dubbo视频教程分享,已更新。
  11. 十大Java编程工具
  12. 【读过的书】《蔡康永的说话之道》
  13. 计算思维的本质是什么
  14. 从语言之争到年龄焦虑
  15. 2019我的公众号回顾
  16. 为什么程序员面试官总喜欢问你有什么技术亮点?
  17. 逆元 与等比数列求和
  18. Python 使用pip安装OpenCV2
  19. 关于福昕软件公司的控件使用-Foxit PDF SDK ActiveX
  20. .NET绘制条码Code128A,Code128B,Code128C,EAN128

热门文章

  1. Wondow10 编译 Wireshark 源码(Windows10 + Vs2019 +Qt5.12)
  2. 聊聊数字化转型与DevOps
  3. 小学四年级计算机制作月历教案,小学信息技术教案制作月历
  4. 微信团购系统网站开发:倍受热捧的“社区团购”知多少?
  5. 清华计算机研究生复试考什么,清华大学计算机硕士研究生考试是走普通流程?就是报名初试复试等。需要做其他准备么?复试前要联络大学么...
  6. mysql查询表和字段的备注信息,并导出到Excel
  7. 【Ware】火绒安全软件怎么禁止其开机自启?
  8. Macbook OSX中拷贝后文件夹和文件都为灰色解决办法
  9. 【vscode基于clangd实现Android/Linux代码跳转】
  10. USACO刷题记录:2020铜组第二题Daisy Chains