Android学习笔记之——基于位置的服务(location-based service)

基于位置的服务简称LBS,随着移动互联网的兴起,这个技术在最近的几年里十分火爆。其实它本身并不是什么时髦的技术,主要的工作原理就是利用无线电通讯网络或GPS等定位方式来确定出移动设备所在的位置,而这种定位技术早在很多年前就已经出现了。

那为什么LBS技术直到最近几年才开始流行呢?这主要是因为,在过去移动设备的功能极其有限,即使定位到了设备所在的位置,也就仅仅只是定位到了而已,我们并不能在位置的基础上进行一些其他的操作。而现在就大大不同了,有了Android系统作为载体,我们可以利用定位出的位置进行许多丰富多彩的操作。比如说天气预报程序可以根据用户所在的位置自动选择城市,发微博的时候我们可以向朋友们晒一下自己在哪里,不认识路的时候随时打开地图就可以查询路线,等等。

基于位置的服务所围绕的核心就是要先确定出用户所在的位置。通常有两种技术方式可以实现:一种是通过GPS定位,一种是通过网络定位。GPS定位的工作原理是基于手机内置的GPS硬件直接和卫星交互来获取当前的经纬度信息,这种定位方式精确度非常高,但缺点是只能在室外使用,室内基本无法接收到卫星的信号。网络定位的工作原理是根据手机当前网络附近的三个基站进行测速,以此计算出手机和每个基站之间的距离,再通过三角定位确定出一个大概的位置,这种定位方式精确度一般,但优点是在室内室外都可以使用。

Android对这两种定位方式都提供了相应的API支持,本博文采用百度原生API来实现LBS

目录

申请API key

获取SHA1

使用百度定位

准备LBS SDK

确定自己位置的经纬度

选择定位模式

更直观的位置信息

使用百度地图

让地图显示出来

让“我”显示在地图上

参考资料


申请API key

要想在自己的应用程序里使用百度的LBS功能,首先必须申请一个API Key。得拥有一个百度账号才能进行申请,登录百度账号,并打开下面的网站。在这里填写一些注册信息即可

http://developer.baidu.com/user/reg

接着访问http://lbsyun.baidu.com/apiconsole/key ,然后同意百度开发者协议,会看到如下图所示的界面。

点击同意后

由于这是一个刚刚注册的账号,所以目前的应用列表是空的。接下来点击创建应用就可以去申请API Key了,应用名称可以随便填,应用类型选择Android SDK,启用服务保持默认即可,如下图所示。

获取SHA1

这个发布版SHA1和开发版SHA1又是个什么东西呢?这是我们申请API Key所必须填写的一个字段,它指的是打包程序时所用签名文件的SHA1指纹,可以通过Android Studio查看到。打开Android Studio中的任意一个项目,点击右侧工具栏的Gradle→项目名→:app→Tasks→android,如下图所示。

这里展示了一个Android Studio项目中所有内置的Gradle Tasks,其中signingReport这个Task就可以用来查看签名文件信息。双击signingReport,结果如图所示。

根本没有找到sha1证书指纹数据。通过在Android Studio中的Terminal中使用keytool获取了,具体如下所示

我这里只出现SHA-256

试试cmd(可以了~)

在控制台内,定位到.android文件夹,输入cd .android

keytool -list -v -keystore debug.keystore

接下来点击提交,应用就应该创建成功了,如下图所示

就是申请到的API Key,有了它就可以进行后续的LBS开发工作了

使用百度定位

现在正是趁热打铁的好时机,新建一个LBSTest项目,包名应该就会自动被命名为com.example.lbstest。

准备LBS SDK

在开始编码之前,我们还需要先将百度LBS开放平台的SDK准备好,下载地址是:

http://lbsyun.baidu.com/index.php?title=sdk/download&action

我们会用到基础地图和定位功能这两个SDK,将它们勾选上,然后点击“开发包”下载按钮即可,

下载完成后对该压缩包解压,其中会有一个libs目录,这里面的内容就是我们所需要的一切了,如图所示。

libs目录下的内容又分为两部分,BaiduLBS_Android.jar这个文件是Java层要使用到的,其他子目录下的so文件是Native层要用到的。so文件是用C/C++语言进行编写,然后再用NDK编译出来的。当然这里我们并不需要去编写C/C++的代码,因为百度都已经做好了封装,但是我们需要将libs目录下的每一个文件都放置到正确的位置。

首先观察一下当前的项目结构,你会发现app模块下面有一个libs目录,这里就是用来存放所有的Jar包的,我们将BaiduLBS_Android.jar复制到这里,如图所示。

接下来展开src/main目录,右击该目录→New→Directory,再创建一个名为jniLibs的目录,这里就是专门用来存放so文件的,然后把压缩包里的其他所有目录直接复制到这里,如图所示(有点类似于opencv)。

另外,虽然所有新创建的项目中,app/build.gradle文件都会默认配置以下这段声明:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

这表示会将libs目录下所有以.jar结尾的文件添加到当前项目的引用中。但是由于我们是直接将Jar包复制到libs目录下的,并没有修改gradle文件,因此不会弹出我们平时熟悉的Sync Now提示。这个时候必须手动点击一下Android Studio顶部工具栏中的Sync按钮,不然项目将无法引用到Jar包中提供的任何接口。(找不到这个按钮,但是可以随意更改一下同步,在删掉同步)

点击Sync按钮之后,libs目录下的jar文件就会多出一个向右的箭头,这就表示项目已经能引用到这些Jar包了

好了,这样我们就把LBS的SDK都准备好了,接下来开始编码吧。

确定自己位置的经纬度

首先修改activity_main.xml中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/position_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</LinearLayout>

布局文件中的内容非常简单,只有一个TextView控件,用于稍后显示当前位置的经纬度信息。

然后修改AndroidManifest.xml文件中的代码,添加权限如下所示:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

这里首先添加了很多行权限声明,每一个权限都是百度LBS SDK内部要用到的。

然后在<application> 标签的内部添加了一个<meta-data> 标签,这个标签的android:name 部分是固定的,必须填com.baidu.lbsapi.API_KEY ,android:value 部分则应该填入前面申请到的API Key。

最后,还需要再注册一个LBS SDK中的服务,

<?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.lbstest">

    <!-- 获取粗略位置 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <!--获取精确gps位置-->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <!-- 获取wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <!-- 获取网络状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 更改wifi连状态 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
    <!-- 读写sd卡 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <!-- 网络链接 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <!--允许程序读写手机状态和身份-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WAKE_LOCK"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

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

        <activity android:name=".MainActivity">
            <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>

接下来修改MainActivity中的代码,如下所示:

package com.example.lbstest;

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

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView positionText;

    public LocationClient mLocationClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //构建一个LocationClient的实例
        mLocationClient=new LocationClient(getApplicationContext());//LocationClient的构建函数接收一个context参数
        //调用getApplicationContext() 方法来获取一个全局的Context 参数并传入

        //调用registerLocationListener方法来注册一个定位的监听器。当获取到位置信息的时候,就会回调这个定位监听器。
        mLocationClient.registerLocationListener(new MyLocationListener());

        positionText=(TextView) findViewById(R.id.position_text_view);

        //如果没有启动下面权限,就询问用户让用户打开
        List<String> permissionList=new ArrayList<>();
        //创建一个空的list集合,然后依次判断这3个权限有没有被授权
        //如果没被授权就添加到List集合中
        //最后将List转换成数组

        //采用运行时权限。对于危险权限是需要进行运行时权限处理的
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED)
        {
            //由于ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION属于同一个权限组,只需申请一个即可
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!permissionList.isEmpty()) {
            String[] permissions = permissionList.toArray(new String[permissionList.size()]);//将List转换成数组
            //调用ActivityCompat.requestPermissions() 方法一次性申请
            ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
        }
        else {
            requestLocation();
        }

    }



    //监听线程,获得当前的经纬度,并显示
    public class MyLocationListener implements BDLocationListener{

        @Override
        public void onReceiveLocation(final BDLocation bdLocation) {
            runOnUiThread(new Runnable() {//不能在主线程(UI线程)以外操作UI,android中把耗时的操作都放到子线程里面
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();//一个可变的字符序列
                    currentPosition.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("经线:").append(bdLocation.getLongitude()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                        currentPosition.append("GPS");
                    }else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation)
                    {
                        currentPosition.append("网络");
                    }
                    positionText.setText(currentPosition);

                }
            });
        }
    }

    //*初始化函数,并启动位置客户端LocationClient*/
    private void requestLocation() {
        initLocation();
        //默认情况下,调用LocationClient的start() 方法只会定位一次
        mLocationClient.start();//调用方法来启动定位。定位的结果会反馈到上面所注册的监听器MyLocationListener中
    }

    private void initLocation(){
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000);//调用setScanSpan方法设置更新的间隔(每5秒会更新一下当前的位置)
        mLocationClient.setLocOption(option);
    }

    //只有同意打开相关权限才可以开启本程序
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){
        //通过一个循环将申请的每个权限都进行了判断,如果有任何一个权限被拒绝
        // 那么就直接调用finish() 方法关闭当前程序,
        // 只有当所有权限都被用户同意了,才会调用requestLocation() 方法开始地理位置定位
        switch(requestCode){
            case 1:
                if (grantResults.length>0){
                    for (int result:grantResults){
                        if (result !=PackageManager.PERMISSION_GRANTED){
                            Toast.makeText(this,"必须同意所有权限才能使用本程序",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }else{
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }


    //在活动被销毁的时候一定要调用LocationClient 的stop() 方法来停止定位,
    //不然程序会持续在后台不停地进行定位,从而严重消耗手机的电量。
    @Override
    protected void onDestroy(){
        super.onDestroy();
        mLocationClient.stop();
    }


}

效果如下图所示

选择定位模式

首先,GPS定位功能必须要由用户主动去启用才行,不然任何应用程序都无法使用GPS获取到手机当前的位置信息。进入手机的设置→位置信息,

可以通过顶部的开关来控制定位功能是开启还是关闭,另外,点击“提高精确度”可以选择具体的定位模式

开启了GPS定位功能之后,可以在initLocation() 方法中对百度LBS SDK的定位模式进行指定,一共有3种模式可选:

  • Hight_Accuracy
  • Battery_Saving
  • Device_Sensors

Hight_Accuracy表示高精确度模式,会在GPS信号正常的情况下优先使用GPS定位,在无法接收GPS信号的时候使用网络定位。Battery_Saving表示节电模式,只会使用网络进行定位。Device_Sensors表示传感器模式,只会使用GPS进行定位。其中,Hight_Accuracy是默认的模式,也就是说,我们即使不修改任何代码,只要拿着手机走到室外去,让手机可以接收到GPS信号,就会自动切换到GPS定位模式了。

更直观的位置信息

package com.example.lbstest;

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

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView positionText;

    public LocationClient mLocationClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //构建一个LocationClient的实例
        mLocationClient=new LocationClient(getApplicationContext());//LocationClient的构建函数接收一个context参数
        //调用getApplicationContext() 方法来获取一个全局的Context 参数并传入

        //调用registerLocationListener方法来注册一个定位的监听器。当获取到位置信息的时候,就会回调这个定位监听器。
        mLocationClient.registerLocationListener(new MyLocationListener());

        positionText=(TextView) findViewById(R.id.position_text_view);

        //如果没有启动下面权限,就询问用户让用户打开
        List<String> permissionList=new ArrayList<>();
        //创建一个空的list集合,然后依次判断这3个权限有没有被授权
        //如果没被授权就添加到List集合中
        //最后将List转换成数组

        //采用运行时权限。对于危险权限是需要进行运行时权限处理的
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED)
        {
            //由于ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION属于同一个权限组,只需申请一个即可
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!permissionList.isEmpty()) {
            String[] permissions = permissionList.toArray(new String[permissionList.size()]);//将List转换成数组
            //调用ActivityCompat.requestPermissions() 方法一次性申请
            ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
        }
        else {
            requestLocation();
        }

    }



    //监听线程,获得当前的经纬度,并显示
    public class MyLocationListener implements BDLocationListener{

        @Override
        public void onReceiveLocation(final BDLocation bdLocation) {
            runOnUiThread(new Runnable() {//不能在主线程(UI线程)以外操作UI,android中把耗时的操作都放到子线程里面
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();//一个可变的字符序列
                    currentPosition.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("经线:").append(bdLocation.getLongitude()).append("\n");

                    //获取地址信息一定需要用到网络
                    // 因此即使我们将定位模式指定成了Device_Sensors,也会自动开启网络定位功能。
                    currentPosition.append("国家:").append(bdLocation.getCountry()).append("\n");
                    currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
                    currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
                    currentPosition.append("区:").append(bdLocation.getDistrict()).append("\n");
                    currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                        currentPosition.append("GPS");
                    }else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation)
                    {
                        currentPosition.append("网络");
                    }
                    positionText.setText(currentPosition);

                }
            });
        }
    }

    //*初始化函数,并启动位置客户端LocationClient*/
    private void requestLocation() {
        initLocation();
        //默认情况下,调用LocationClient的start() 方法只会定位一次
        mLocationClient.start();//调用方法来启动定位。定位的结果会反馈到上面所注册的监听器MyLocationListener中
    }

    private void initLocation(){
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000);//调用setScanSpan方法设置更新的间隔(每5秒会更新一下当前的位置)
        //调用了setLocationMode() 方法来将定位模式指定成传感器模式,也就是说只能使用GPS进行定位
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        option.setIsNeedAddress(true);//表示我们需要获取当前位置详细的地址信息。
        mLocationClient.setLocOption(option);
    }

    //只有同意打开相关权限才可以开启本程序
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){
        //通过一个循环将申请的每个权限都进行了判断,如果有任何一个权限被拒绝
        // 那么就直接调用finish() 方法关闭当前程序,
        // 只有当所有权限都被用户同意了,才会调用requestLocation() 方法开始地理位置定位
        switch(requestCode){
            case 1:
                if (grantResults.length>0){
                    for (int result:grantResults){
                        if (result !=PackageManager.PERMISSION_GRANTED){
                            Toast.makeText(this,"必须同意所有权限才能使用本程序",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }else{
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }


    //在活动被销毁的时候一定要调用LocationClient 的stop() 方法来停止定位,
    //不然程序会持续在后台不停地进行定位,从而严重消耗手机的电量。
    @Override
    protected void onDestroy(){
        super.onDestroy();
        mLocationClient.stop();
    }


}

使用百度地图

让地图显示出来

先修改activity_main.xml中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/position_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

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

</LinearLayout>

这里在布局文件中新放置了一个MapView控件,并让它填充满整个屏幕。这个MapView是由百度提供的自定义控件,所以在使用它的时候需要将完整的包名加上。同时,如果不需要前面的textView的信息,可以增加下面语句将其去掉

android:visibility="gone"

接下来修改MainActivity中的代码,如下所示:

package com.example.lbstest;

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

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.MapView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView positionText;

    public LocationClient mLocationClient;

    private MapView mapView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        SDKInitializer.initialize(getApplicationContext());//先通过initialize方法来进行初始化操作
        //initialize() 方法接收一个Context 参数
        // 这里我们调用getApplicationContext() 方法来获取一个全局的Context 参数并传入
        //注意初始化操作一定要在setContentView() 方法前调用,不然的话就会出错

        setContentView(R.layout.activity_main);

        //构建一个LocationClient的实例
        mLocationClient=new LocationClient(getApplicationContext());//LocationClient的构建函数接收一个context参数
        //调用getApplicationContext() 方法来获取一个全局的Context 参数并传入

        //调用registerLocationListener方法来注册一个定位的监听器。当获取到位置信息的时候,就会回调这个定位监听器。
        mLocationClient.registerLocationListener(new MyLocationListener());

        positionText=(TextView) findViewById(R.id.position_text_view);

        mapView=(MapView) findViewById(R.id.bmapView);//找到MapView实例

        //如果没有启动下面权限,就询问用户让用户打开
        List<String> permissionList=new ArrayList<>();
        //创建一个空的list集合,然后依次判断这3个权限有没有被授权
        //如果没被授权就添加到List集合中
        //最后将List转换成数组

        //采用运行时权限。对于危险权限是需要进行运行时权限处理的
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED)
        {
            //由于ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION属于同一个权限组,只需申请一个即可
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!permissionList.isEmpty()) {
            String[] permissions = permissionList.toArray(new String[permissionList.size()]);//将List转换成数组
            //调用ActivityCompat.requestPermissions() 方法一次性申请
            ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
        }
        else {
            requestLocation();
        }

    }



    //监听线程,获得当前的经纬度,并显示
    public class MyLocationListener implements BDLocationListener{

        @Override
        public void onReceiveLocation(final BDLocation bdLocation) {
            runOnUiThread(new Runnable() {//不能在主线程(UI线程)以外操作UI,android中把耗时的操作都放到子线程里面
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();//一个可变的字符序列
                    currentPosition.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("经线:").append(bdLocation.getLongitude()).append("\n");

                    //获取地址信息一定需要用到网络
                    // 因此即使我们将定位模式指定成了Device_Sensors,也会自动开启网络定位功能。
                    currentPosition.append("国家:").append(bdLocation.getCountry()).append("\n");
                    currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
                    currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
                    currentPosition.append("区:").append(bdLocation.getDistrict()).append("\n");
                    currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                        currentPosition.append("GPS");
                    }else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation)
                    {
                        currentPosition.append("网络");
                    }
                    positionText.setText(currentPosition);

                }
            });
        }
    }

    //*初始化函数,并启动位置客户端LocationClient*/
    private void requestLocation() {
        initLocation();
        //默认情况下,调用LocationClient的start() 方法只会定位一次
        mLocationClient.start();//调用方法来启动定位。定位的结果会反馈到上面所注册的监听器MyLocationListener中
    }

    private void initLocation(){
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000);//调用setScanSpan方法设置更新的间隔(每5秒会更新一下当前的位置)
        //调用了setLocationMode() 方法来将定位模式指定成传感器模式,也就是说只能使用GPS进行定位
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        option.setIsNeedAddress(true);//表示我们需要获取当前位置详细的地址信息。
        mLocationClient.setLocOption(option);
    }

    //只有同意打开相关权限才可以开启本程序
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){
        //通过一个循环将申请的每个权限都进行了判断,如果有任何一个权限被拒绝
        // 那么就直接调用finish() 方法关闭当前程序,
        // 只有当所有权限都被用户同意了,才会调用requestLocation() 方法开始地理位置定位
        switch(requestCode){
            case 1:
                if (grantResults.length>0){
                    for (int result:grantResults){
                        if (result !=PackageManager.PERMISSION_GRANTED){
                            Toast.makeText(this,"必须同意所有权限才能使用本程序",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }else{
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }



    //这个方法在活动准备好和用户进行交互的时候调用。
    // 此时的活动一定位于返回栈的栈顶,并且处于运行状态。
    @Override
    protected void onResume(){
        super.onResume();
        mapView.onResume();
    }

    //这个方法在系统准备去启动或者恢复另一个活动的时候调用。
    // 通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,
    // 但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
    @Override
    protected void onPause(){
        super.onPause();
        mapView.onPause();
    }

    //在活动被销毁的时候一定要调用LocationClient 的stop() 方法来停止定位,
    //不然程序会持续在后台不停地进行定位,从而严重消耗手机的电量。
    @Override
    protected void onDestroy(){
        super.onDestroy();
        mLocationClient.stop();
        mapView.onDestroy();
    }


}

地图是成功显示出来了,但也许这并不是你想要的。因为这是一张默认的地图,显示的是北京市中心的位置,而你可能希望看到更加精细的地图信息,比如说自己所在位置的周边环境。显然,通过缩放和移动的方式来慢慢找到自己的位置是一种很愚蠢的做法。那么本小节我们就来学习一下,如何才能在地图中快速移动到自己的位置。

百度LBS SDK的API中提供了一个BaiduMap 类,它是地图的总控制器,调用MapView的getMap()方法就能获取到BaiduMap 的实例,如下所示:

BaiduMap baiduMap = mapView.getMap();

有了BaiduMap后,我们就能对地图进行各种各样的操作了,比如设置地图的缩放级别以及将地图移动到某一个经纬度上。

百度地图将缩放级别的取值范围限定在3到19之间,其中小数点位的值也是可以取的,值越大,地图显示的信息就越精细。比如我们想要将缩放级别设置成12.5,就可以这样写:

MapStatusUpdate update = MapStatusUpdateFactory.zoomTo(12.5f);
baiduMap.animateMapStatus(update);

其中MapStatusUpdateFactory的zoomTo() 方法接收一个float 型的参数,就是用于设置缩放级别的,这里传入12.5f。zoomTo() 方法返回一个MapStatusUpdate 对象,我们把这个对象传入BaiduMap的animateMapStatus() 方法当中即可完成缩放功能。

那么怎样才能让地图移动到某一个经纬度上呢?这就需要借助LatLng 类了。其实LatLng并没有什么太多的用法,主要就是用于存放经纬度值的,它的构造方法接收两个参数,第一个参数是纬度值,第二个参数是经度值。之后调用MapStatusUpdateFactory的newLatLng() 方法将LatLng 对象传入,newLatLng() 方法返回的也是一个MapStatusUpdate 对象,我们再把这个对象传入BaiduMap的animateMapStatus() 方法当中,就可以将地图移动到指定的经纬度上了,写法如下:

LatLng ll = new LatLng(39.915, 116.404);
MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
baiduMap.animateMapStatus(update);

上述代码就实现了将地图移动到北纬39.915度、东经116.404度这个位置的功能。

代码如下

package com.example.lbstest;

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

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.model.LatLng;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView positionText;

    public LocationClient mLocationClient;

    private MapView mapView;

    private BaiduMap baiduMap;
    private boolean isFirstLocate=true;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        SDKInitializer.initialize(getApplicationContext());//先通过initialize方法来进行初始化操作
        //initialize() 方法接收一个Context 参数
        // 这里我们调用getApplicationContext() 方法来获取一个全局的Context 参数并传入
        //注意初始化操作一定要在setContentView() 方法前调用,不然的话就会出错

        setContentView(R.layout.activity_main);

        //构建一个LocationClient的实例
        mLocationClient=new LocationClient(getApplicationContext());//LocationClient的构建函数接收一个context参数
        //调用getApplicationContext() 方法来获取一个全局的Context 参数并传入

        //调用registerLocationListener方法来注册一个定位的监听器。当获取到位置信息的时候,就会回调这个定位监听器。
        mLocationClient.registerLocationListener(new MyLocationListener());

        positionText=(TextView) findViewById(R.id.position_text_view);

        mapView=(MapView) findViewById(R.id.bmapView);//找到MapView实例

        //百度LBS SDK的API中提供了一个BaiduMap 类,它是地图的总控制器,
        // 调用MapView的getMap()方法就能获取到BaiduMap 的实例
        baiduMap = mapView.getMap();



        //***************权限********************
        //如果没有启动下面权限,就询问用户让用户打开
        List<String> permissionList=new ArrayList<>();
        //创建一个空的list集合,然后依次判断这3个权限有没有被授权
        //如果没被授权就添加到List集合中
        //最后将List转换成数组

        //采用运行时权限。对于危险权限是需要进行运行时权限处理的
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED)
        {
            //由于ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION属于同一个权限组,只需申请一个即可
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!permissionList.isEmpty()) {
            String[] permissions = permissionList.toArray(new String[permissionList.size()]);//将List转换成数组
            //调用ActivityCompat.requestPermissions() 方法一次性申请
            ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
        }
        else {
            requestLocation();
        }

    }



    //监听线程,获得当前的经纬度,并显示
    public class MyLocationListener implements BDLocationListener{

        @Override
        public void onReceiveLocation(final BDLocation bdLocation) {

            if(bdLocation.getLocType()==BDLocation.TypeNetWorkLocation||bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                navigateTo(bdLocation);
            }

            runOnUiThread(new Runnable() {//不能在主线程(UI线程)以外操作UI,android中把耗时的操作都放到子线程里面
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();//一个可变的字符序列
                    currentPosition.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("经线:").append(bdLocation.getLongitude()).append("\n");

                    //获取地址信息一定需要用到网络
                    // 因此即使我们将定位模式指定成了Device_Sensors,也会自动开启网络定位功能。
                    currentPosition.append("国家:").append(bdLocation.getCountry()).append("\n");
                    currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
                    currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
                    currentPosition.append("区:").append(bdLocation.getDistrict()).append("\n");
                    currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                        currentPosition.append("GPS");
                    }else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation)
                    {
                        currentPosition.append("网络");
                    }
                    positionText.setText(currentPosition);

                }
            });
        }
    }

    private void navigateTo(BDLocation location){
        //先是将BDLocation 对象中的地理位置信息取出并封装到LatLng 对象中
        if(isFirstLocate){
            //************将地图调到当前位置**********
            LatLng ll=new LatLng(location.getLatitude(),location.getLongitude());//用于存放经纬度
            //调用MapStatusUpdateFactory的newLatLng() 方法将LatLng 对象传入,
            // newLatLng() 方法返回的也是一个MapStatusUpdate 对象
            MapStatusUpdate update= MapStatusUpdateFactory.newLatLng(ll);
            // 再把这个对象传入BaiduMap的animateMapStatus() 方法当中,就可以将地图移动到指定的经纬度上了
            baiduMap.animateMapStatus(update);

            //**************将地图缩放*******
            //MapStatusUpdateFactory的zoomTo() 方法接收一个float 型的参数,就是用于设置缩放级别的
            update=MapStatusUpdateFactory.zoomTo(19f);
            //zoomTo() 方法返回一个MapStatusUpdate 对象,我们把这个对象传入BaiduMap的animateMapStatus() 方法当中即可完成缩放功能。
            baiduMap.animateMapStatus(update);

            isFirstLocate=false;//只有首次需要做初始化设置


        }
    }

    //*初始化函数,并启动位置客户端LocationClient*/
    private void requestLocation() {
        initLocation();
        //默认情况下,调用LocationClient的start() 方法只会定位一次
        mLocationClient.start();//调用方法来启动定位。定位的结果会反馈到上面所注册的监听器MyLocationListener中
    }

    private void initLocation(){
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000);//调用setScanSpan方法设置更新的间隔(每5秒会更新一下当前的位置)
        //调用了setLocationMode() 方法来将定位模式指定成传感器模式,也就是说只能使用GPS进行定位
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        option.setIsNeedAddress(true);//表示我们需要获取当前位置详细的地址信息。
        mLocationClient.setLocOption(option);
    }

    //只有同意打开相关权限才可以开启本程序
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){
        //通过一个循环将申请的每个权限都进行了判断,如果有任何一个权限被拒绝
        // 那么就直接调用finish() 方法关闭当前程序,
        // 只有当所有权限都被用户同意了,才会调用requestLocation() 方法开始地理位置定位
        switch(requestCode){
            case 1:
                if (grantResults.length>0){
                    for (int result:grantResults){
                        if (result !=PackageManager.PERMISSION_GRANTED){
                            Toast.makeText(this,"必须同意所有权限才能使用本程序",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }else{
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }



    //这个方法在活动准备好和用户进行交互的时候调用。
    // 此时的活动一定位于返回栈的栈顶,并且处于运行状态。
    @Override
    protected void onResume(){
        super.onResume();
        mapView.onResume();
    }

    //这个方法在系统准备去启动或者恢复另一个活动的时候调用。
    // 通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,
    // 但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
    @Override
    protected void onPause(){
        super.onPause();
        mapView.onPause();
    }

    //在活动被销毁的时候一定要调用LocationClient 的stop() 方法来停止定位,
    //不然程序会持续在后台不停地进行定位,从而严重消耗手机的电量。
    @Override
    protected void onDestroy(){
        super.onDestroy();
        mLocationClient.stop();
        mapView.onDestroy();
    }


}

结果如下图所示

让“我”显示在地图上

现在我们已经可以让地图显示我们周边的环境了,但是相信在你平时使用手机地图时应该会注意到,通常情况下手机地图上应该都会有一个小光标,用于显示设备当前所在的位置,并且如果设备正在移动的话,那么这个光标也会跟着一起移动。那么我们现在就继续对现有代码进行扩展,让“我”能够显示在地图上。

百度LBS SDK当中提供了一个MyLocationData.Builder 类,这个类是用来封装设备当前所在位置的,我们只需将经纬度信息传入到这个类的相应方法当中就可以了,如下所示:

MyLocationData.Builder locationBuilder = new MyLocationData.Builder();
locationBuilder.latitude(39.915);
locationBuilder.longitude(116.404);

MyLocationData.Builder 类还提供了一个build() 方法,当我们把要封装的信息都设置完成之后,只需要调用它的build() 方法,就会生成一个MyLocationData的实例,然后再将这个实例传入到BaiduMap的setMyLocationData() 方法当中,就可以让设备当前的位置显示在地图上了,写法如下:

MyLocationData locationData = locationBuilder.build();
baiduMap.setMyLocationData(locationData);

修改MainActivity中的代码,如下所示:

package com.example.lbstest;

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

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import com.baidu.location.BDLocation;
import com.baidu.location.BDLocationListener;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BaiduMap;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.model.LatLng;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView positionText;

    public LocationClient mLocationClient;

    private MapView mapView;

    private BaiduMap baiduMap;
    private boolean isFirstLocate=true;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        SDKInitializer.initialize(getApplicationContext());//先通过initialize方法来进行初始化操作
        //initialize() 方法接收一个Context 参数
        // 这里我们调用getApplicationContext() 方法来获取一个全局的Context 参数并传入
        //注意初始化操作一定要在setContentView() 方法前调用,不然的话就会出错

        setContentView(R.layout.activity_main);

        //构建一个LocationClient的实例
        mLocationClient=new LocationClient(getApplicationContext());//LocationClient的构建函数接收一个context参数
        //调用getApplicationContext() 方法来获取一个全局的Context 参数并传入

        //调用registerLocationListener方法来注册一个定位的监听器。当获取到位置信息的时候,就会回调这个定位监听器。
        mLocationClient.registerLocationListener(new MyLocationListener());

        positionText=(TextView) findViewById(R.id.position_text_view);

        mapView=(MapView) findViewById(R.id.bmapView);//找到MapView实例

        //百度LBS SDK的API中提供了一个BaiduMap 类,它是地图的总控制器,
        // 调用MapView的getMap()方法就能获取到BaiduMap 的实例
        baiduMap = mapView.getMap();

        //如果要在地图上显示自己的位置,需要先调用下面方法来开启这个功能
        baiduMap.setMyLocationEnabled(true);



        //***************权限********************
        //如果没有启动下面权限,就询问用户让用户打开
        List<String> permissionList=new ArrayList<>();
        //创建一个空的list集合,然后依次判断这3个权限有没有被授权
        //如果没被授权就添加到List集合中
        //最后将List转换成数组

        //采用运行时权限。对于危险权限是需要进行运行时权限处理的
        if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED)
        {
            //由于ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION属于同一个权限组,只需申请一个即可
            permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.READ_PHONE_STATE)!= PackageManager.PERMISSION_GRANTED)
        {
            permissionList.add(Manifest.permission.READ_PHONE_STATE);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (!permissionList.isEmpty()) {
            String[] permissions = permissionList.toArray(new String[permissionList.size()]);//将List转换成数组
            //调用ActivityCompat.requestPermissions() 方法一次性申请
            ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
        }
        else {
            requestLocation();
        }

    }



    //监听线程,获得当前的经纬度,并显示
    public class MyLocationListener implements BDLocationListener{

        @Override
        public void onReceiveLocation(final BDLocation bdLocation) {

            if(bdLocation.getLocType()==BDLocation.TypeNetWorkLocation||bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                navigateTo(bdLocation);
            }

            runOnUiThread(new Runnable() {//不能在主线程(UI线程)以外操作UI,android中把耗时的操作都放到子线程里面
                @Override
                public void run() {
                    StringBuilder currentPosition = new StringBuilder();//一个可变的字符序列
                    currentPosition.append("纬度:").append(bdLocation.getLatitude()).append("\n");
                    currentPosition.append("经线:").append(bdLocation.getLongitude()).append("\n");

                    //获取地址信息一定需要用到网络
                    // 因此即使我们将定位模式指定成了Device_Sensors,也会自动开启网络定位功能。
                    currentPosition.append("国家:").append(bdLocation.getCountry()).append("\n");
                    currentPosition.append("省:").append(bdLocation.getProvince()).append("\n");
                    currentPosition.append("市:").append(bdLocation.getCity()).append("\n");
                    currentPosition.append("区:").append(bdLocation.getDistrict()).append("\n");
                    currentPosition.append("街道:").append(bdLocation.getStreet()).append("\n");
                    currentPosition.append("定位方式:");
                    if (bdLocation.getLocType()==BDLocation.TypeGpsLocation){
                        currentPosition.append("GPS");
                    }else if (bdLocation.getLocType()==BDLocation.TypeNetWorkLocation)
                    {
                        currentPosition.append("网络");
                    }
                    positionText.setText(currentPosition);

                }
            });
        }
    }

    private void navigateTo(BDLocation location){
        //先是将BDLocation 对象中的地理位置信息取出并封装到LatLng 对象中
        if(isFirstLocate){
            //************将地图调到当前位置**********
            LatLng ll=new LatLng(location.getLatitude(),location.getLongitude());//用于存放经纬度
            //调用MapStatusUpdateFactory的newLatLng() 方法将LatLng 对象传入,
            // newLatLng() 方法返回的也是一个MapStatusUpdate 对象
            MapStatusUpdate update= MapStatusUpdateFactory.newLatLng(ll);
            // 再把这个对象传入BaiduMap的animateMapStatus() 方法当中,就可以将地图移动到指定的经纬度上了
            baiduMap.animateMapStatus(update);

            //**************将地图缩放*******
            //MapStatusUpdateFactory的zoomTo() 方法接收一个float 型的参数,就是用于设置缩放级别的
            update=MapStatusUpdateFactory.zoomTo(19f);
            //zoomTo() 方法返回一个MapStatusUpdate 对象,我们把这个对象传入BaiduMap的animateMapStatus() 方法当中即可完成缩放功能。
            baiduMap.animateMapStatus(update);

            isFirstLocate=false;//只有首次需要做初始化设置
        }

        //***********将自己当前的位置显示出来**********
        //MyLocationData.Builder 类,这个类是用来封装设备当前所在位置的,只需将经纬度信息传入到这个类的相应方法当中就可以了
        MyLocationData.Builder locationBuilder=new MyLocationData.Builder();
        locationBuilder.latitude(location.getLatitude());
        locationBuilder.longitude(location.getLongitude());
        //调用MyLocationData.Builder 类所提供的build() 方法,
        // 就会生成一个MyLocationData的实例
        MyLocationData locationData=locationBuilder.build();
        //再将这个实例传入到BaiduMap的setMyLocationData() 方法当中,就可以让设备当前的位置显示在地图上了
        baiduMap.setMyLocationData(locationData);


    }

    //*初始化函数,并启动位置客户端LocationClient*/
    private void requestLocation() {
        initLocation();
        //默认情况下,调用LocationClient的start() 方法只会定位一次
        mLocationClient.start();//调用方法来启动定位。定位的结果会反馈到上面所注册的监听器MyLocationListener中
    }

    private void initLocation(){
        LocationClientOption option = new LocationClientOption();
        option.setScanSpan(5000);//调用setScanSpan方法设置更新的间隔(每5秒会更新一下当前的位置)
        //调用了setLocationMode() 方法来将定位模式指定成传感器模式,也就是说只能使用GPS进行定位
        option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);
        option.setIsNeedAddress(true);//表示我们需要获取当前位置详细的地址信息。
        mLocationClient.setLocOption(option);
    }

    //只有同意打开相关权限才可以开启本程序
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions,int[] grantResults){
        //通过一个循环将申请的每个权限都进行了判断,如果有任何一个权限被拒绝
        // 那么就直接调用finish() 方法关闭当前程序,
        // 只有当所有权限都被用户同意了,才会调用requestLocation() 方法开始地理位置定位
        switch(requestCode){
            case 1:
                if (grantResults.length>0){
                    for (int result:grantResults){
                        if (result !=PackageManager.PERMISSION_GRANTED){
                            Toast.makeText(this,"必须同意所有权限才能使用本程序",Toast.LENGTH_SHORT).show();
                            finish();
                            return;
                        }
                    }
                    requestLocation();
                }else{
                    Toast.makeText(this, "发生未知错误", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }



    //这个方法在活动准备好和用户进行交互的时候调用。
    // 此时的活动一定位于返回栈的栈顶,并且处于运行状态。
    @Override
    protected void onResume(){
        super.onResume();
        mapView.onResume();
    }

    //这个方法在系统准备去启动或者恢复另一个活动的时候调用。
    // 通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,
    // 但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
    @Override
    protected void onPause(){
        super.onPause();
        mapView.onPause();
    }

    //在活动被销毁的时候一定要调用LocationClient 的stop() 方法来停止定位,
    //不然程序会持续在后台不停地进行定位,从而严重消耗手机的电量。
    @Override
    protected void onDestroy(){
        super.onDestroy();
        mLocationClient.stop();
        mapView.onDestroy();
        baiduMap.setMyLocationEnabled(false);//程序退出时,关闭在地图上显示自己位置的功能
    }


}

其他的开发指南可以参考:http://lbsyun.baidu.com/

参考资料

https://blog.csdn.net/kezhongke/article/details/42678077(sha1证书指纹数据)

https://blog.csdn.net/yeluoweiluo615/article/details/54020678(sha1证书指纹数据)

https://blog.csdn.net/Hanghang_/article/details/87207093?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%AE%89%E5%8D%93%E7%AC%AC%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81%20%E4%BD%BF%E7%94%A8%E7%99%BE%E5%BA%A6%E5%AE%9A%E4%BD%8D&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-87207093

猜你喜欢

转载自blog.csdn.net/gwplovekimi/article/details/106711340