利用百度地图开源代码实现定位和实时路径跟踪

利用百度地图开源代码实现定位和实时路径跟踪

更新更新(2020.8.19)
今天刚调试出来了,关于把前面的模拟实时路径跟踪部分修改成了监听手机内部GPS的实时跟踪,能拿着手机在室外跑来跑去,然后定时回调定位监听器的方法,获取到新的经纬度信息,然后边画线,能实现实时路径跟踪的功能了!!!

先给大家演示一下效果

在这里插入图片描述
这里最左边的点,是因为一开始跟踪的时候,会接收一个经纬度,但是可能是因为一开始接收GPS信号的时候不太准确,所以会有一个偏差,而且还是偏差有点大的,但是后面的话,相对来说还是大致可以描绘出来我行走的路线的,这个也是GPS定位本来就不太精准的原因吧?

实现实时路径跟踪,我这里是把原来调用模拟位置的函数,改成了回调监听定位函数的代码,就是不断通过重新获取当前经纬度的监听函数,再把这个获取到的点跟前一个点通过画线的方法进行连线;大概这样就能实现实时监听了。

上代码:(这个就是mainActivity文件,其他的xml文件啥的都没变化)

package com.example.map_track;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Address;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.CoordType;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
import com.baidu.mapapi.map.InfoWindow;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.Marker;
import com.baidu.mapapi.map.MarkerOptions;
import com.baidu.mapapi.map.MyLocationConfiguration;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.map.Overlay;
import com.baidu.mapapi.map.OverlayOptions;
import com.baidu.mapapi.map.Polyline;
import com.baidu.mapapi.map.PolylineOptions;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.geocode.GeoCodeResult;
import com.baidu.mapapi.search.geocode.GeoCoder;
import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {

    public static final int PLAYBACK_OVER = 1;
    private MapView mapView;
    private BaiduMap baiduMap;
    private LocationClient mLocationClient = null;  //定义一个监听器对象
    private MyLocationListener myLocationListener;
    private double currentLat, currentLng;  //当前的经纬度
    private String currentAddr;   //当前所在的地址
    private DatabaseAdapter dbAdapter;
    private GeoCoder geoCoder;
    private int currentTrackLineID; //当前跟踪的路线ID
   // BDLocation location;

    boolean flag = true;

    boolean isFirstLocation = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        mapView = (MapView) findViewById(R.id.bmapView);
        initBaiduMap();     //初始化地图,显示我的当前位置
        dbAdapter = new DatabaseAdapter(this);
    }
    /**
     * 初始化百度地图
     */
    LocationClientOption option = new LocationClientOption();
    private void initBaiduMap() {
        baiduMap = mapView.getMap();
        baiduMap.setMyLocationEnabled(true);    //打开定位图层,允许定位
        mLocationClient = new LocationClient(getApplicationContext());  //声明LocationClient类
        myLocationListener = new MyLocationListener();

        mLocationClient.registerLocationListener(myLocationListener);   //注册监听函数


        //LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); //设置定位模式
        option.setOpenGps(true);

        option.setCoorType("bd09ll");   //返回的定位结果是百度经纬度,默认值gcj02
        option.setScanSpan(1000);  //设置发起定位请求的间隔为5000ms
        option.setIsNeedAddress(true);  //返回的定位结果包含地址信息
        option.setNeedDeviceDirect(true);   //返回的定位结果包含手机机头方向
        option.setWifiCacheTimeOut(5*60*1000);
        option.setNeedNewVersionRgc(true);
        mLocationClient.setLocOption(option);
        mLocationClient.start();    //启动SDK定位
        mLocationClient.requestLocation();  //发起定位请求
        isFirstLocation = true;

        //用于转换地理编码的监听器
        geoCoder = GeoCoder.newInstance();
        geoCoder.setOnGetGeoCodeResultListener(new OnGetGeoCoderResultListener() {
            @Override
            public void onGetReverseGeoCodeResult(ReverseGeoCodeResult result) {
                if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) {
                    //没有检索结果
                }
                else {
                    //获取地理编码结果
                    //System.out.println(result.getAddress());
                    currentAddr = result.getAddress();
                    //更新线路的结束位置
                    dbAdapter.updateEndLoc(currentAddr, currentTrackLineID);
                }
            }

            @Override
            public void onGetGeoCodeResult(GeoCodeResult arg0) {

            }
        });
    }

    double cur_Lat = 0;
    double cur_Lng = 0;

    BDLocation location1;

    /**
     * 定位的监听
     */
    class MyLocationListener extends BDAbstractLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
            if (location != null && flag) {
                flag = false;
                currentLat = location.getLatitude();    //当前的纬度
                currentLng = location.getLongitude();   //当前的经度
                currentAddr = location.getAddrStr();     //当前位置的地址
            //    cur_Lat = currentLat;
            //    cur_Lng = currentLng;

                Log.i("定位的纬度 = ", String.valueOf(currentLat));
                Log.i("定位的经度 = ", String.valueOf(currentLng));

                if (isFirstLocation) {

                    isFirstLocation = false;

                    //System.out.println("currentAddr=" + currentAddr);
                    //构造我的当前位置信息
                    MyLocationData.Builder builder = new MyLocationData.Builder();
                    builder.latitude(location.getLatitude());   //设置纬度
                    builder.longitude(location.getLongitude()); //设置经度
                    builder.accuracy(location.getRadius()); //设置精度(半径)
                    builder.direction(location.getDirection()); //设置方向
                    builder.speed(location.getSpeed()); //设置速度
                    MyLocationData locationData = builder.build();

                    //把我的位置信息设置到地图上
                    baiduMap.setMyLocationData(locationData);
                    //配置我的位置
                    LatLng latlng = new LatLng(currentLat , currentLng);
                    baiduMap.setMyLocationConfiguration(new MyLocationConfiguration(MyLocationConfiguration.LocationMode.FOLLOWING,true,null));
                    //设置我的位置为地图的中心点,并且把地图放大16倍(缩放级别为3-20)
                    baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLngZoom(latlng, 20));

                    System.out.println("地址:"+currentAddr+"---纬度:"+currentLat+"---经度:"+currentLng+"\n");
                    Log.i("hello2",currentAddr);
                    Log.i("hello3", String.valueOf(currentLat));
                    Log.i("hello4", String.valueOf(currentLng));
                    Log.i("hello1", String.valueOf(location));
                }


            }
        }

        private void getlocation() {


          //  currentLat = cur_Lat;
          //  currentLng = cur_Lng;
            Log.i("现在纬度 = ", String.valueOf(cur_Lat));
            Log.i("现在经度 = ", String.valueOf(cur_Lng));
            //     baiduMap.setMyLocationEnabled(false);
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    //功能菜单项
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.mylocation :
                mylocation();   //我的位置
                break;
            case R.id.start_track :
                startTrack();   //开始跟踪
                break;
            case R.id.end_track :
                endTrack();     //结束跟踪
                break;
            //       case R.id.track_back :
            //      trackBack();    //跟踪回放
            //          break;

            default:
                break;
        }
        return true;
    }

 
    /**
     * 结束跟踪
     */
    private void endTrack() {
        isTracking = false;  //结束线程
        Toast.makeText(MainActivity.this, "跟踪结束...", Toast.LENGTH_SHORT).show();
        //转换地理编码,把最后的一个经纬度转换成地址(把经纬度转化成对应的地名地址,或者把地名地址转换成对应的经纬度)
        geoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(new LatLng(currentLat, currentLng)));
    }

    /**
     * 我的位置
     */
    private void mylocation() {
        Toast.makeText(MainActivity.this, "正在定位中...", Toast.LENGTH_SHORT).show();
        flag = true;
        baiduMap.clear();   //清除地图上自定义的图层
        baiduMap.setMyLocationEnabled(true);
        mLocationClient.requestLocation();  //发起定位请求
    }

    /**
     * 开始跟踪功能
     */
    private void startTrack() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("线路跟踪");
        builder.setCancelable(true);
        final View view = getLayoutInflater().inflate(R.layout.add_track_line_dialog, null);
        builder.setView(view);
        builder.setPositiveButton("添加", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                EditText et_track_name = (EditText) view.findViewById(R.id.editText1_track_name);
                String trackName = et_track_name.getText().toString();
                System.out.println(trackName);
                createTrack(trackName);     //创建线路跟踪
                Toast.makeText(MainActivity.this, "跟踪开始...", Toast.LENGTH_SHORT).show();
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.show();
    }

    private boolean isTracking = false;

    //创建一条线路跟踪
    private void createTrack(String trackName) {
        Track track = new Track();
        track.setTrack_name(trackName);
        track.setCreate_date(DateUtils.toDate(new Date())); //创建时间为当前的时间
        track.setStart_loc(currentAddr);
        currentTrackLineID = dbAdapter.addTrack(track);   //添加线路,返回线路ID
        dbAdapter.addTrackDetail(currentTrackLineID, currentLat, currentLng);   //添加明细信息
        baiduMap.clear();
        addOverlay();
        list.add(new LatLng(currentLat, currentLng));
        isTracking = true;   //线程模拟的标记
        System.out.println(list);
        new Thread(new TrackThread()).start();
    }

    //在地图上添加图层(线路的每一个点)
    private void addOverlay() {
        //--------添加一个标注覆盖物在当前位置--------
        //构建Marker图标
        baiduMap.setMyLocationEnabled(false);   //关闭定位图层
        BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.mipmap.icon_markx);
        //构建MakerOption,用于在地图上添加Marker
        LatLng latlng = new LatLng(currentLat, currentLng);
        OverlayOptions option = new MarkerOptions().position(latlng).icon(bitmap);
        //在地图上添加Marker,并显示
        baiduMap.addOverlay(option);
        //把当前添加的地图位置作为地图的中心点
        baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLng(latlng));
    }

    //用于存储两个相邻点的经纬度,再画线
    private ArrayList<LatLng> list = new ArrayList<LatLng>();

    //模拟跟踪的线程
    class TrackThread implements Runnable {
        @Override
        public void run() {
            while (isTracking) {
           //     BDLocation location3 = new BDLocation();
                //myLocationListener.getlocation();
                baiduMap.setMyLocationEnabled(true);
                flag = true;
                myLocationListener.onReceiveLocation(location1);
                baiduMap.setMyLocationEnabled(false);
                Log.i("准备画线前的纬度 =  ", String.valueOf(currentLat));
                Log.i("准备画线前的经度 = ", String.valueOf(currentLng));

                dbAdapter.addTrackDetail(currentTrackLineID, currentLat, currentLng);
                addOverlay();
                list.add(new LatLng(currentLat, currentLng));
                drawLine(); //画线
                System.out.println("drawLine");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("线程休眠失败");
                }
              //  Log.i("hello", String.valueOf(location5));
            }
        }
    }


    //在两个点之间画线
    private void drawLine() {
        OverlayOptions lineOptions = new PolylineOptions().points(list).color(0xFFFF0000);
        baiduMap.addOverlay(lineOptions);
        list.remove(0); //这里是为了移除两个点之间的前一个,因为在第一次画线之后,每一次画线都只需要添加第二个点,但是它这里是默认把两个点都添加进去,再画线,所以需要删除第一个点
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //在Activity执行onResume时执行mMapView.onResume(),实现地图生命周期管理
        mapView.onDestroy();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //在Activity执行onResume时执行mMapView.onResume(),实现地图生命周期管理
        mapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //在Activity执行onPause时执行mMapView.onPause(),实现地图生命周期管理
        mapView.onPause();
    }


}

程序还是有一点问题的,我这里设置了一个标志位isFirstLocation,这样每次回调定位监听函数的时候,就只会接收新的经纬度信息,但是因为设置了这个标志位,所以后面结束跟踪之后,就不能重新把当前的位置定位到地图的中心点;

这个问题的话,我是想着可以设置一个按钮,每次点击这个按钮的时候,就把地图clear一下,把图层都清除了,然后再重新初始化一下定位的方法,这样大概就能实现重新定位到当前位置了。

以上是我这几天调程序的思路,有什么不对的地方,还需要大神们帮忙指出来呀!!网上有demo是采用的鹰眼服务的,但是怎么去调用鹰眼服务这一块,我还没有搞懂,所以我是直接通过的定时回调定位监听函数的方法实现的。

如果有大神知道怎么使用鹰眼服务的话,可以给我提供一些思路吗?


8月18更新:
现在还在卡着,对于怎么把模拟经纬度变化的函数转换成监听手机内部GPS信号的回调函数,这一块我还是没有想明白,我后面是加上了鹰眼服务,但是运行起来还是调用逻辑上有问题,一开始跟踪就会闪退,有大神可以给我提供一些意见和思路吗?


这段时间需要用Android Studio做一个基于百度地图的app,记录一下过程,主要实现了定位和实时路径跟踪。

先演示一下效果:(视频放不了,图片凑凑数)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实时路径的思想和步骤:
思想:如果需要在地图上实现实时跟踪,那么首先就需要实现定位功能,在实现了定位功能的基础上,需要在地图上画出行走轨迹,那么如何画出行走轨迹?就需要实时地记录行走的经纬度,两点之间画一条直线,中间设置定位的频率高的话,那么记录的经纬度就越多,这样就可以让记录的行走路径光滑一些。
如果需要回放功能,就需要建立一个数据库将行走线路存储下来,数据库就应该要一条线路对应多个点的关系,所以需要两张表,一个表是记录的线路,另一个表是记录的一条线路对应的多个点。(回放功能我还没有实现

先添加一下权限和密钥之类的吧,先修改好manifests.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.map_track"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <!-- 这个权限用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>
    <!-- 这个权限用于访问GPS定位-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
    <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位-->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
    <!--  获取运营商信息,用于支持提供运营商信息相关的接口  -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" ></uses-permission>
    <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>
    <!--  用于读取手机当前的状态  -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" ></uses-permission>
    <!-- 写外置存储。如果开发者使用了离线地图,并且数据写在外置存储区域,则需要申请该权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" ></uses-permission>
    <!-- 访问网络,进行地图相关业务数据请求,包括地图数据,路线规划,POI检索等 -->
    <uses-permission android:name="android.permission.INTERNET" ></uses-permission>
    <!--  SD卡读取权限,用户写入离线定位数据  -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
    <!--  允许应用读取低级别的系统日志文件-->
    <uses-permission android:name="android.permission.READ_LOGS"></uses-permission>
    <!-- 读取外置存储。如果开发者使用了so动态加载功能并且把so文件放在了外置存储区域,则需要申请该权限,否则不需要 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"  />

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    
    <application

        android:allowBackup="true"
        android:icon="@drawable/ic_launcher_background"
        android:label="线路跟踪案例"
        android:theme="@style/AppTheme" >

        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="Dt7DLSza4fP0NBGMEMjF2IRNaOtXqqn6" ></meta-data>

        <activity
            android:name="com.example.map_track.MainActivity"
            android:label="线路跟踪案例" >

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote"> </service>

    </application>

</manifest>

再就是设置一下布局文件activity_main.xml文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    tools:context=".MainActivity">

    <!--
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    -->


    <com.baidu.mapapi.map.MapView
        android:id="@+id/bmapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true" />

</RelativeLayout>

(以下几个类都是在app/java/com.example.map_track包中完成)
在这里插入图片描述

首先就要建立一个线路类Track,用于存储每条线路的信息;

package com.example.map_track;

import java.util.ArrayList;

/**
 *线路跟踪对象
 **/

public class Track {    //线路
    private int id;
    private String track_name;
    private String create_date;
    private String start_loc;
    private String end_loc;
    private ArrayList<TrackDetail> trackDetails;

    public ArrayList<TrackDetail> getTrackDetails() {
        return trackDetails;
    }
    public void setTrackDetails(ArrayList<TrackDetail> trackDetails) {
        this.trackDetails = trackDetails;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getTrack_name() {
        return track_name;
    }
    public void setTrack_name(String track_name) {
        this.track_name = track_name;
    }
    public String getCreate_date() {
        return create_date;
    }
    public void setCreate_date(String create_date) {
        this.create_date = create_date;
    }
    public String getStart_loc() {
        return start_loc;
    }
    public void setStart_loc(String start_loc) {
        this.start_loc = start_loc;
    }
    public  String getEnd_loc() {
        return end_loc;
    }
    public  void setEnd_loc(String end_loc) {
        this.end_loc = end_loc;
    }
    public Track(int id, String track_name, String create_date,String start_loc, String end_loc) {
        super();
        this.id = id;
        this.track_name = track_name;
        this.create_date = create_date;
        this.start_loc = start_loc;
        this.end_loc = end_loc;
    }

    public Track(String track_name, String create_date, String start_loc, String end_loc) {
        super();
        this.track_name = track_name;
        this.create_date = create_date;
        this.start_loc = start_loc;
        this.end_loc = end_loc;
    }

    public Track() {
        super();
    }
}

线路类Track建立好了之后,还需要建立一个路线结点类TrackDetail,用于存储线路上每个点的信息;

package com.example.map_track;

public class  TrackDetail {
    private int id;
    private double lat;
    private double lng;
    private Track track;
    public int getId() {
        return id;
    }
    public void setId(int id) { this.id = id; }
    public double getLat() {
        return lat;
    }
    public void setLat(double lat) {
        this.lat = lat;
    }
    public double getLng() {
        return lng;
    }
    public void setLng(double lng) {
        this.lng = lng;
    }
    public Track getTrack() {
        return track;
    }
    public void setTrack(Track track) {
        this.track = track;
    }
    public TrackDetail(int id, double lat, double lng) {
        super();
        this.id = id;
        this.lat = lat;
        this.lng = lng;
    }
}

还需要设置一个数据库助手类DatabaseHelper:

package com.example.map_track;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 数据助手类
 **/
public class DatabaseHelper extends SQLiteOpenHelper {

    private  static String DB_NAME = "track.db";
    //表名
    public  static String TABLE_TRACK = "track";
    public static String TABLE_TRACK_DETAIL = "track_detail";

    //字段
    public static String ID = "_id";
    //跟踪表
    public static String TRACK_NAME = "track_name";
    public static String CREATE_DATE = "create_date";
    public static String START_LOC = "start_loc";
    public static String END_LOC = "end_loc";

    //明细表
    public static String TID = "tid";   //线路的ID
    public static String LAT = "lat";   //纬度
    public static String LNG = "lng";   //经度

    private static String CREATE_TABLE_TRACK = "create table track(_id integer primary key autoincrement,track_name text,create_date text,start_loc text,end_loc text)";
    private static String CREATE_TABLE_TRACK_DETAIL = "create table track_detail(_id integer primary key autoincrement,tid integer not null,lat real,lng real)";

    private static int VERSION = 1;

    public DatabaseHelper(Context context) {
        super(context,DB_NAME,null,VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE_TRACK);
        db.execSQL(CREATE_TABLE_TRACK_DETAIL);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion) {
            db.execSQL("drop table if exists track");
            db.execSQL("drop table if exists track_detail");
            db.execSQL(CREATE_TABLE_TRACK);
            db.execSQL(CREATE_TABLE_TRACK_DETAIL);
        }
    }
}

再需要建立一个databaseAdapter类,用于数据库的增删改查:

package com.example.map_track;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import java.util.ArrayList;

/**
 *数据库适配器
 **/
public class DatabaseAdapter {
    private DatabaseHelper dbHelper;

    public DatabaseAdapter(Context context) {
        dbHelper = new DatabaseHelper(context);
    }

    //添加线路跟踪
    public  int addTrack(Track track) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        //String sql = "insert to track(track_name,create_date,start_loc,end_loc) values(?,?,?,?)";
        //db.exceSQL(sql,new Object[]{track.getTrack_name(),track.getCreate_date(),track.getStart_loc(),track.getEnd_loc()});
        ContentValues values = new ContentValues();
        values.put(DatabaseHelper.TRACK_NAME, track.getTrack_name());
        values.put(DatabaseHelper.CREATE_DATE, track.getCreate_date());
        values.put(DatabaseHelper.START_LOC, track.getStart_loc());
        values.put(DatabaseHelper.END_LOC, track.getEnd_loc());
        long id = db.insertOrThrow(DatabaseHelper.TABLE_TRACK, null, values);
        db.close();
        return (int) id;
    }

    /**
     *更新终点地址
     **/
    //刚开始走的时候,起点和终点是同一个位置,当结束的时候,终点才变了,这个时候需要一个更新终点位置的函数
    public void updateEndLoc(String endLoc, int id) {
        String sql = "update track set end_loc=? where _id=?";
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.execSQL(sql, new Object[] {endLoc, id});
        db.close();
    }

    //添加路线跟踪明细
    public void addTrackDetail(int tid, double lat, double lng ) {  //tid为外键
        SQLiteDatabase db= dbHelper.getWritableDatabase();
        String sql = "insert into track_detail(tid,lat,lng) values(?,?,?)";
        db.execSQL(sql, new Object[] {tid ,lat, lng});
        db.close();
    }

    //根据ID查询线路跟踪
    public ArrayList<TrackDetail> getTrackDetails(int id) {
        String sql = "select _id,lat,lng from track_detail where tid=? order by _id desc";
        ArrayList<TrackDetail> list = new ArrayList<TrackDetail>();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor c= db.rawQuery(sql, new String[] { String.valueOf(id) });
        if (c != null) {
            TrackDetail detail = null;
            while (c.moveToNext()) {
                detail = new TrackDetail(c.getInt(0), c.getDouble(1),c.getDouble(2));
                list.add(detail);
            }
            c.close();
        }
        return list;
    }

    /**
     查询所有线路
     **/
    public  ArrayList<Track> getTracks() {
        ArrayList<Track> tracks = new ArrayList<Track>();
        String sql = "select _id,track_name,create_date,start_loc,end_loc from track ";
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor c = db.rawQuery(sql, null);
        Track t = null;
        if (c != null) {
            while (c.moveToNext()) {
                t = new Track(c.getInt(0), c.getString(1), c.getString(2), c.getString(3), c.getString(4));
                tracks.add(t);
            }
            c.close();
        }
        db.close();
        return tracks;
    }

    //根据ID删除线路跟踪
    public void delTrack(int id) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        String sql1 = "delete from track where _id=?";
        String sql2 = "delete from track_detail where tid=?";
        try {
            db.beginTransaction();
            db.execSQL(sql2, new Object[] { id });
            db.execSQL(sql1, new Object[] { id });
            db.setTransactionSuccessful();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            db.endTransaction();
            if( db != null)
                db.close();
        }

    }
}


再需要设置一个DateUtils类,用于日期转换:

package com.example.map_track;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *日期转换的工具类
 **/

public class DateUtils {

    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static String toDate(Date date) {
        return sdf.format(date);
    }
}

接下来就是核心的类MainActivity:

package com.example.map_track;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.CoordType;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
import com.baidu.mapapi.map.InfoWindow;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.Marker;
import com.baidu.mapapi.map.MarkerOptions;
import com.baidu.mapapi.map.MyLocationConfiguration;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.map.Overlay;
import com.baidu.mapapi.map.OverlayOptions;
import com.baidu.mapapi.map.Polyline;
import com.baidu.mapapi.map.PolylineOptions;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.search.core.SearchResult;
import com.baidu.mapapi.search.geocode.GeoCodeResult;
import com.baidu.mapapi.search.geocode.GeoCoder;
import com.baidu.mapapi.search.geocode.OnGetGeoCoderResultListener;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeOption;
import com.baidu.mapapi.search.geocode.ReverseGeoCodeResult;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {

    public static final int PLAYBACK_OVER = 1;
    private MapView mapView;
    private BaiduMap baiduMap;
    private LocationClient mLocationClient = null;  //定义一个监听器对象
    private MyLocationListener myLocationListener;
    private double currentLat, currentLng;  //当前的经纬度
    private String currentAddr;   //当前所在的地址
    private DatabaseAdapter dbAdapter;
    private GeoCoder geoCoder;
    private int currentTrackLineID; //当前跟踪的路线ID



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        mapView = (MapView) findViewById(R.id.bmapView);
        initBaiduMap();     //初始化地图,显示我的当前位置
        dbAdapter = new DatabaseAdapter(this);
    }
    /**
     * 初始化百度地图
     */
    private void initBaiduMap() {
        baiduMap = mapView.getMap();
        baiduMap.setMyLocationEnabled(true);    //打开定位图层,允许定位
        mLocationClient = new LocationClient(getApplicationContext());  //声明LocationClient类
        myLocationListener = new MyLocationListener();
        mLocationClient.registerLocationListener(myLocationListener);   //注册监听函数

        LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); //设置定位模式
        option.setCoorType("bd09ll");   //返回的定位结果是百度经纬度,默认值gcj02
        option.setScanSpan(5000);  //设置发起定位请求的间隔为5000ms
        option.setIsNeedAddress(true);  //返回的定位结果包含地址信息
        option.setNeedDeviceDirect(true);   //返回的定位结果包含手机机头方向
        mLocationClient.setLocOption(option);
        mLocationClient.start();    //启动SDK定位
        mLocationClient.requestLocation();  //发起定位请求

//用于转换地理编码的监听器
        geoCoder = GeoCoder.newInstance();
        geoCoder.setOnGetGeoCodeResultListener(new OnGetGeoCoderResultListener() {
            @Override
            public void onGetReverseGeoCodeResult(ReverseGeoCodeResult result) {
                if (result == null || result.error != SearchResult.ERRORNO.NO_ERROR) {
                    //没有检索结果
                }
                else {
                    //获取地理编码结果
                    //System.out.println(result.getAddress());
                    currentAddr = result.getAddress();
                    //更新线路的结束位置
                    dbAdapter.updateEndLoc(currentAddr, currentTrackLineID);
                }
            }

            @Override
            public void onGetGeoCodeResult(GeoCodeResult arg0) {

            }
        });
    }
    boolean flag = true;

    /**
     * 定位的监听
     */
    class MyLocationListener extends BDAbstractLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
            if (location != null && flag) {
                flag = false;
                currentLat = location.getLatitude();    //当前的纬度
                currentLng = location.getLongitude();   //当前的经度
                currentAddr = location.getAddrStr();     //当前位置的地址
                //System.out.println("currentAddr=" + currentAddr);
                //构造我的当前位置信息
                MyLocationData.Builder builder = new MyLocationData.Builder();
                builder.latitude(location.getLatitude());   //设置纬度
                builder.longitude(location.getLongitude()); //设置经度
                builder.accuracy(location.getRadius()); //设置精度(半径)
                builder.direction(location.getDirection()); //设置方向
                builder.speed(location.getSpeed()); //设置速度
                MyLocationData locationData = builder.build();

                //把我的位置信息设置到地图上
                baiduMap.setMyLocationData(locationData);
                //配置我的位置
                LatLng latlng = new LatLng(currentLat , currentLng);
                baiduMap.setMyLocationConfiguration(new MyLocationConfiguration(MyLocationConfiguration.LocationMode.FOLLOWING,true,null));
                //设置我的位置为地图的中心点,并且把地图放大16倍(缩放级别为3-20)
                baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLngZoom(latlng, 20));

                //System.out.println("地址:"+currentAddr+"---纬度:"+currentLat+"---经度:"+currentLng+"\n");



            }
        }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    //功能菜单项
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.mylocation :
                mylocation();   //我的位置
                break;
            case R.id.start_track :
                startTrack();   //开始跟踪
                break;
            case R.id.end_track :
                endTrack();     //结束跟踪
                break;
            //       case R.id.track_back :
            //      trackBack();    //跟踪回放
            //          break;

            default:
                break;
        }
        return true;
    }



    /**
     * 结束跟踪
     */
    private void endTrack() {
        isTracking = false;  //结束线程
        Toast.makeText(MainActivity.this, "跟踪结束...", Toast.LENGTH_SHORT).show();
        //转换地理编码,把最后的一个经纬度转换成地址(把经纬度转化成对应的地名地址,或者把地名地址转换成对应的经纬度)
        geoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(new LatLng(currentLat, currentLng)));
    }

    /**
     * 我的位置
     */
    private void mylocation() {
        Toast.makeText(MainActivity.this, "正在定位中...", Toast.LENGTH_SHORT).show();
        flag = true;
        baiduMap.clear();   //清除地图上自定义的图层
        baiduMap.setMyLocationEnabled(true);
        mLocationClient.requestLocation();  //发起定位请求
    }

    /**
     * 开始跟踪功能
     */
    private void startTrack() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("线路跟踪");
        builder.setCancelable(true);
        final View view = getLayoutInflater().inflate(R.layout.add_track_line_dialog, null);
        builder.setView(view);
        builder.setPositiveButton("添加", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                EditText et_track_name = (EditText) view.findViewById(R.id.editText1_track_name);
                String trackName = et_track_name.getText().toString();
                System.out.println(trackName);
                createTrack(trackName);     //创建线路跟踪
                Toast.makeText(MainActivity.this, "跟踪开始...", Toast.LENGTH_SHORT).show();
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.show();
    }

    //创建一条线路跟踪
    private void createTrack(String trackName) {
        Track track = new Track();
        track.setTrack_name(trackName);
        track.setCreate_date(DateUtils.toDate(new Date())); //创建时间为当前的时间
        track.setStart_loc(currentAddr);
        currentTrackLineID = dbAdapter.addTrack(track);   //添加线路,返回线路ID
        dbAdapter.addTrackDetail(currentTrackLineID, currentLat, currentLng);   //添加明细信息
//        baiduMap.clear();
        addOverlay();
        list.add(new LatLng(currentLat, currentLng));
        isTracking = true;   //线程模拟的标记
        System.out.println(list);
        new Thread(new TrackThread()).start();
    }

    private boolean isTracking = false;

    //模拟跟踪的线程
    class TrackThread implements Runnable {
        @Override
        public void run() {
            while (isTracking) {
                getLocation();  //获取模拟位置
                dbAdapter.addTrackDetail(currentTrackLineID, currentLat, currentLng);
                addOverlay();
                list.add(new LatLng(currentLat, currentLng));
                drawLine(); //画线
                System.out.println("drawLine");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //用于存储两个相邻点的经纬度,再画线
    private ArrayList<LatLng> list = new ArrayList<LatLng>();

    //在地图上添加图层(线路的每一个点)
    private void addOverlay() {
        //--------添加一个标注覆盖物在当前位置--------
        //构建Marker图标
        baiduMap.setMyLocationEnabled(false);   //关闭定位图层
        BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher_round);
        //构建MakerOption,用于在地图上添加Marker
        LatLng latlng = new LatLng(currentLat, currentLng);
        OverlayOptions option = new MarkerOptions().position(latlng).icon(bitmap);
        //在地图上添加Marker,并显示
        baiduMap.addOverlay(option);
        //把当前添加的地图位置作为地图的中心点
        baiduMap.animateMapStatus(MapStatusUpdateFactory.newLatLng(latlng));
    }

    //在两个点之间画线
    private void drawLine() {
        OverlayOptions lineOptions = new PolylineOptions().points(list).color(0xFFFF0000);
        baiduMap.addOverlay(lineOptions);
        list.remove(0); //这里是为了移除两个点之间的前一个,因为在第一次画线之后,每一次画线都只需要添加第二个点,但是它这里是默认把两个点都添加进去,再画线,所以需要删除第一个点
    }

    /**
     * 模拟位置,真实的时候应该使用监听器来获取地理位置信息
     */
    private void getLocation() {
        currentLat = currentLat + Math.random() / 1000;
        currentLng = currentLng + Math.random() / 1000;
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //在Activity执行onResume时执行mMapView.onResume(),实现地图生命周期管理
        mapView.onDestroy();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //在Activity执行onResume时执行mMapView.onResume(),实现地图生命周期管理
        mapView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        //在Activity执行onPause时执行mMapView.onPause(),实现地图生命周期管理
        mapView.onPause();
    }
}

这里要设置一个菜单项,当点击菜单项的时候,就可以弹出多个选项,实现对应的功能:
需要在app/res下创建一个menu文件夹,在menu下再创建一个main.xml文件;

<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/mylocation"
        android:orderInCategory="10"
        app:showAsAction="never"
        android:title="我的位置" />
    <item
        android:id="@+id/start_track"
        android:orderInCategory="100"
        android:title="开始跟踪"
        app:showAsAction="never" />
    <item
        android:id="@+id/end_track"
        android:orderInCategory="200"
        android:title="结束跟踪"
        app:showAsAction="never" />
    <item
        android:id="@+id/track_back"
        android:orderInCategory="300"
        android:title="跟踪回放"
        app:showAsAction="never" />

</menu>

MainActivity的大致执行过程:
首先就是SDK的初始化,然后初始化地图,也就是调用initBaiduMap方法,设置定位的模式,发起定位请求, 在请求的MyLocationListener回调方法里面,就可以得到经纬度信息,并显示在我的位置上;
接下来就是点击startTrack(),这是开始跟踪的方法,这里会调用add_track_line_dialog布局文件,所以还需要在res/layout文件夹中创建一个add_track_line_dialog.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    android:orientation="vertical" >


    <EditText
        android:id="@+id/editText1_track_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入跟踪线路名称"
        android:ems="10"
        android:layout_marginBottom="5dp" >

        <requestFocus />
    </EditText>
    
    <EditText
        android:id="@+id/editText2_start_loc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="起始位置"

        android:cursorVisible="false"
        android:focusable="false"
        android:focusableInTouchMode="false"

        android:ems="10" />



</LinearLayout>

接下来会调用createTrack方法,这是一个创建线路跟踪的方法;然后会去调用addOverlay方法,这个是添加一个标注覆盖物在当前位置,每在线路上画一个线段的时候,就把当前的经纬度用一个覆盖物标注出来;
再创建一个模拟线程TrackThread(),这时候会去调用getLocation()方法,这里是采取的模拟位置,即利用当前位置和一个随机数来确定一个新的经纬度;
确定好新的位置之后,就调用画线方法drawLine(),把两个点用线连接起来;

(其他的功能还没有实现,例如跟踪回放,这个在类中已经写好了,剩下的就是需要在MainActivity中添加对应的回调函数)
我这里的线路,是采用的模拟线路,还需要把它给改好,改成监听手机内部GPS的方法,大概就可以拿着手机到外面实际测试一下是否可行了

这是刚开始学的,有什么错误的地方还需要大佬们帮忙指出来呀


  • 0
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值