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

       转载请标明地址: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/50404941https://github.com/gaoleiandroid1201/DrawerLayout

3、位置图标不跟着自己运动,查看自己代码也没有问题啊,调试了好久,跑了不少冤枉路,莫名其妙

解决方法:把百度sdk中的初始化地图和配置参数等代码拷进来代替,果然奏效

4、进程保活,这个不能完全保活,只能尽量保活,除非是被加入白名单或自启应用像微信,但是有些 app像行者和咕咚就算app被杀死了也能活下来,有关进程保活可参考:https://mp.weixin.qq.com/s/d3scy-dC46NW9sz7wc3YLQhttps://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

  • 22
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 27
    评论
要实现车辆轨迹回放,你需要先获取车辆行驶轨迹的数据,可以通过GPS或其他位置数据采集设备获取。接下来,你可以使用百度地图API提供的Polyline类来绘制轨迹线路,使用Marker类来标注车辆行驶的起点和终点,使用DrivingRoute类来绘制车辆行驶的路线。最后,你可以使用定时器或其他方式来控制车辆行驶过程中的速度和动画效果。 下面是一个使用百度地图API实现车辆轨迹回放的示例代码: ```javascript //创建地图实例 var map = new BMap.Map("container"); //设置地图中心点和缩放级别 map.centerAndZoom(new BMap.Point(116.404, 39.915), 15); //开启鼠标滚轮缩放 map.enableScrollWheelZoom(true); //定义轨迹数据 var path = [ {"lng":116.404,"lat":39.915}, {"lng":116.415,"lat":39.915}, {"lng":116.425,"lat":39.915}, {"lng":116.435,"lat":39.915}, {"lng":116.445,"lat":39.915}, {"lng":116.455,"lat":39.915}, {"lng":116.465,"lat":39.915}, {"lng":116.475,"lat":39.915}, {"lng":116.485,"lat":39.915}, {"lng":116.495,"lat":39.915} ]; //绘制轨迹线路 var polyline = new BMap.Polyline(path, {strokeColor:"blue", strokeWeight:6, strokeOpacity:0.5}); map.addOverlay(polyline); //标注起点和终点 var startMarker = new BMap.Marker(path[0]); var endMarker = new BMap.Marker(path[path.length-1]); map.addOverlay(startMarker); map.addOverlay(endMarker); //绘制车辆行驶路线 var driving = new BMap.DrivingRoute(map, {renderOptions:{map: map, autoViewport: true}}); driving.search(new BMap.Point(path[0].lng, path[0].lat), new BMap.Point(path[path.length-1].lng, path[path.length-1].lat)); //控制车辆行驶过程中的速度和动画效果 var index = 0; var timer = setInterval(function(){ if(index < path.length-1){ var p1 = new BMap.Point(path[index].lng, path[index].lat); var p2 = new BMap.Point(path[index+1].lng, path[index+1].lat); var angle = getAngle(p1, p2); startMarker.setRotation(angle); startMarker.setPosition(p2); index++; }else{ clearInterval(timer); } }, 1000); //计算两点之间的方向角度 function getAngle(p1, p2){ var dx = p2.lng - p1.lng; var dy = p2.lat - p1.lat; var angle = 0; if(dx == 0){ angle = dy > 0 ? 90 : 270; }else if(dy == 0){ angle = dx > 0 ? 0 : 180; }else{ angle = Math.atan(dy/dx) * 180 / Math.PI; if(dx < 0){ angle += 180; }else if(dy < 0){ angle += 360; } } return angle; } ``` 在上面的示例代码中,我们先定义了一个包含轨迹数据的数组path,然后创建了一个地图实例,并设置了地图中心点和缩放级别。接下来,我们使用Polyline类来绘制轨迹线路,使用Marker类来标注车辆行驶的起点和终点,使用DrivingRoute类来绘制车辆行驶的路线。最后,我们使用定时器来控制车辆行驶过程中的速度和动画效果。 需要注意的是,上面的示例代码中并没有使用实际的GPS或位置数据,而是直接使用了一个包含经纬度坐标的数组。在实际应用中,你需要根据实际情况获取真实的位置数据,并将其转换为经纬度坐标,然后再进行轨迹回放的绘制。
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值