Bootstrap

Android Studio3.0对于百度地图SDK的开发(基于方向传感器实现手机朝向显示)



前言

上午刚说不知道怎么绑定方向传感器,下午就找到教程了……
主要参考了花海ipa大大的这篇文章
2019-详细Android Studio开发百度地图(3)—百度地图_配合方向感应器的定位功能的实现
之前对于定位的方向,是通过getDirection()来实现的,说是获取手机朝向,但是你不到处走动,图标的朝向一直不变,也不知道是它是怎么运行的。既然自带的方法不行,那就自己通过方向传感器获取一个方向。
在这里插入图片描述


添加传感器类

右键存放MainActivity.java文件的那个文件夹,New中选择Java Class,命名为MyOrientationListener
在这里插入图片描述
以下为代码,复制粘贴即可(注意文件最顶端有一个package,不要覆盖了)

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class MyOrientationListener implements SensorEventListener
{
    private SensorManager mSensorManager;
    private Context mContext;
    private Sensor mSensor;
    private float lastX;
    public MyOrientationListener(Context context)
    {
        this.mContext = context;
    }
    @SuppressWarnings("deprecation")
    public void start()
    {
        mSensorManager = (SensorManager) mContext
                .getSystemService(Context.SENSOR_SERVICE);
        if (mSensorManager != null)
        {
            // 获得方向传感器
            mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
        }

        if (mSensor != null)
        {
            mSensorManager.registerListener(this, mSensor,
                    SensorManager.SENSOR_DELAY_UI);
        }
    }

    public void stop()
    {
        mSensorManager.unregisterListener(this);
    }

    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1)
    {
        // TODO Auto-generated method stub

    }

    @SuppressWarnings(
            { "deprecation" })
    @Override
    public void onSensorChanged(SensorEvent event)
    {
        if (event.sensor.getType() == Sensor.TYPE_ORIENTATION)
        {
            float x = event.values[SensorManager.DATA_X];

            if (Math.abs(x - lastX) > 1.0)
            {
                if (mOnOrientationListener != null)
                {
                    mOnOrientationListener.onOrientationChanged(x);
                }
            }

            lastX = x;

        }
    }

    private OnOrientationListener mOnOrientationListener;

    public void setOnOrientationListener(
            OnOrientationListener mOnOrientationListener)
    {
        this.mOnOrientationListener = mOnOrientationListener;
    }

    public interface OnOrientationListener
    {
        void onOrientationChanged(float x);
    }

}

修改MainActivity.java

添加新对象

//方向传感器
    private MyOrientationListener mMyOrientationListener;
    private float mCurrentX;

初始化传感器

//传感器
    private void initOrientation() {
        //传感器
        mMyOrientationListener = new MyOrientationListener(getApplicationContext());
        mMyOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {
            @Override
            public void onOrientationChanged(float x) {
                mCurrentX = x;
            }
        });
    }

在修改初始化地图函数

private void initmap() throws Exception {
        //定位初始化
        mLocationClient = new LocationClient(getApplicationContext());
        mLocationClient.registerLocationListener(new MyLocationListener());
        //获取地图控件引用
        mMapView = (MapView) findViewById(R.id.bmapView);
        mBaiduMap = mMapView.getMap();
        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder().zoom(20).build()));
        //开启地图的定位图层
        mBaiduMap.setMyLocationEnabled(true);
        //通过LocationClientOption设置LocationClient相关参数
        LocationClientOption option = new LocationClientOption();
        option.setOpenGps(true); // 打开gps
        option.setCoorType("bd09ll"); // 设置坐标类型
        option.setIsNeedAddress(true);//设置是否需要地址信息
        option.setScanSpan(1000);
        //设置locationClientOption
        mLocationClient.setLocOption(option);
        //注册LocationListener监听器
        MyLocationListener myLocationListener = new MyLocationListener();
        mLocationClient.registerLocationListener(myLocationListener);
        //新增注册监听函数
        initOrientation();
        //开启地图定位图层
        mLocationClient.start();
    }

修改onReceiveLocation函数,将direction中的参数换成方向传感器获取到的方向。

MyLocationData locData = new MyLocationData.Builder()
                    .accuracy(location.getRadius())
                    // 此处设置开发者获取到的方向信息,顺时针0-360
                    .direction(mCurrentX).latitude(location.getLatitude())
                    .longitude(location.getLongitude()).build();

管理生命周期

protected void onStart() {
        super.onStart();
        //开启定位
        mBaiduMap.setMyLocationEnabled(true);
        if (!mLocationClient.isStarted())
            mLocationClient.start();
        //开启方向传感器
        mMyOrientationListener.start();
    }
    @Override
    protected void onStop() {
        super.onStop();
        //停止定位
        mBaiduMap.setMyLocationEnabled(false);
        mLocationClient.stop();
        //停止方向传感器
        mMyOrientationListener.stop();
    }

测试

在这里插入图片描述

由于SDK支持的最短刷新间隔就是1000ms(一秒),所以看起来会一卡一卡的,而且由于每次定位顺带把界面中心也移动到了定位点,导致想看看周围却发现不管怎么拖动地图,一会地图又回来了。


优化

平滑方向变化

虽然百度地图SDK的定位是一秒一次的,但是我们的运动传感器不是一秒一次获取方向的。根据代码我们可以看到,只要方向变化超过1°,就会传一次方向数据。

public void onSensorChanged(SensorEvent event)
    {
        if (event.sensor.getType() == Sensor.TYPE_ORIENTATION)
        {
            float x = event.values[SensorManager.DATA_X];
            if (Math.abs(x - lastX) > 1.0)
            {
                if (mOnOrientationListener != null)
                {
                   mOnOrientationListener.onOrientationChanged(x);
                }
            }
            lastX = x;
        }
    }

那么,我们就在传回方向的时候重新修改定位点数据。

新增对象

//记录当前经纬度以及定位精度
private double Latitude;
private double Longitude;
private float Radius;

获取上一次定位数据

在onReceiveLocation中添加以下代码

Latitude=location.getLatitude();
Longitude=location.getLongitude();
Radius=location.getRadius();

修改initOrientation

 //传感器
    private void initOrientation() {
        //传感器
        mMyOrientationListener = new MyOrientationListener(getApplicationContext());
        mMyOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {
            @Override
            public void onOrientationChanged(float x) {
                mCurrentX = x;
                MyLocationData locData = new MyLocationData.Builder()
                        .accuracy(Radius)
                        .direction(mCurrentX).latitude(Latitude)
                        .longitude(Longitude).build();
                mBaiduMap.setMyLocationData(locData);
            }
        });
    }

这样就能在方向变化时立即反应到地图上。

再次定位,画面中心点不再回到定位点

新增对象

//是否为初次定位
private boolean isFirst = true;

在onReceiveLocation中加入判断条件

if(isFirst)
            {
                mBaiduMap.animateMapStatus(update);
                MyLocationConfiguration configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL,
                        true, null );
mBaiduMap.setMyLocationConfiguration(configuration);
                isFirst=false;
            }

这样,只有第一次定位重置地图位置。

迅速返回定位点

有时候划太远了,可能会找不到自己在哪。因此新增一个按钮,点击就让地图中心点回到定位点。

添加按钮

在activity_main.xml中添加一个按钮控件

<Button
        android:id="@+id/but_Loc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="回到定位点"
        android:layout_marginLeft="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="127dp"/>

实现按钮

public class MainActivity extends AppCompatActivity后加上implements View.OnClickListener
在这里插入图片描述
实现按钮功能

 //按钮响应
    private void button() {
        //按钮
        Button mbut_Loc = (Button) findViewById(R.id.but_Loc);
        //按钮处理
        mbut_Loc.setOnClickListener(this);
    }
    //回到定位点
    private void centerToMyLocation() {
        LatLng latLng = new LatLng(Latitude, Longitude);
        MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng);
        mBaiduMap.animateMapStatus(update);
    }
    @Override
    public void onClick(View v) {
        SDKInitializer.initialize(getApplicationContext());
        switch (v.getId()) {
            case R.id.but_Loc: {
                centerToMyLocation();
                break;
            }
        }
    }

最后在initmap()的最后加上一句button();,这样点击按钮就能回到定位点了。

定位点模式切换

添加单选框

在activity_main.xml中添加RadioGroup

 <RadioGroup
        android:id="@+id/Mode"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="#000000"
        app:layout_constraintStart_toStartOf="@+id/bmapView"
        app:layout_constraintTop_toTopOf="@+id/bmapView">

        <RadioButton
            android:id="@+id/following"
            android:layout_width="137dp"
            android:layout_height="wrap_content"
            android:text="跟随态" />

        <RadioButton
            android:id="@+id/normal"
            android:layout_width="137dp"
            android:layout_height="wrap_content"
            android:text="普通态"
            android:checked="true"/>

        <RadioButton
            android:id="@+id/compass"
            android:layout_width="137dp"
            android:layout_height="wrap_content"
            android:text="罗盘态" />
    </RadioGroup>

实现功能

添加新对象

//定位模式
    private RadioGroup mode;

初始化函数

public void initRadioGroup() {
        //获取radiongroup对象
        mode = (RadioGroup) findViewById(R.id.Mode);
        //通过radiogroup的setoncheckedlistener方法注册监听事件
        //在监听事件中创建oncheckedlistener 在重写oncheckedchanged方法
        mode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                //获取被选中的radiobutton的id
                RadioButton rcheck = (RadioButton) findViewById(checkedId);
                //获取
                String checkText = rcheck.getText().toString();
                MyLocationConfiguration configuration= null;
                switch (checkText)
                {
                    case "跟随态":
                        configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.FOLLOWING,
                                true, null );
                        mBaiduMap.setMyLocationConfiguration(configuration);
                        //罗盘态会改变百度地图的旋转和俯视角度,切换到其他模式需要重置
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).rotate(0).build()));
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).overlook(0).build()));
                        break;
                    case "普通态":
                        configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL,
                                true, null );
                        mBaiduMap.setMyLocationConfiguration(configuration);
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).rotate(0).build()));
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).overlook(0).build()));
                        break;
                    case "罗盘态":
                        configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.COMPASS,
                                true, null );
                        mBaiduMap.setMyLocationConfiguration(configuration);
                        break;
                }
            }
        });
    }

在onCreate()的setContentView(R.layout.activity_main);后添加initRadioGroup();
测试
在这里插入图片描述
可以流畅运行了。

源码

最后附上源代码

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
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.MapStatus;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.MyLocationConfiguration;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.model.LatLng;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private MapView mMapView = null;
    private BaiduMap mBaiduMap = null;
    private LocationClient mLocationClient = null;

    //方向传感器
    private MyOrientationListener mMyOrientationListener;
    private float mCurrentX;
    private double Latitude;
    private double Longitude;
    private float Radius;
    //是否为初次定位
    private boolean isFirst = true;
    //定位模式
    private RadioGroup mode;

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //检查权限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
        {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
        }else if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
        {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
        }else if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
        {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE}, 3);
        } else {
            initSDK(true);
            setContentView(R.layout.activity_main);
            initRadioGroup();
            try {
                initmap();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "没有ACCESS_FINE_LOCATION权限!", Toast.LENGTH_LONG).show();
                    finish();
                }
                break;
            case 2:
                if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "没有ACCESS_COARSE_LOCATION权限!", Toast.LENGTH_LONG).show();
                    finish();
                }
                break;
            case 3:
                if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "没有WRITE_EXTERNAL_STORAGE权限!", Toast.LENGTH_LONG).show();
                    finish();
                }
                break;
        }
        initSDK(true);
        setContentView(R.layout.activity_main);
        initRadioGroup();
        try {
            initmap();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void initSDK(boolean status) {
        LocationClient.setAgreePrivacy(status);
        SDKInitializer.setAgreePrivacy(getApplicationContext(), status);
        try {
            //在使用SDK各组件之前初始化context信息,传入ApplicationContext
            SDKInitializer.initialize(getApplicationContext());
            //自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
            //包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
            SDKInitializer.setCoordType(CoordType.BD09LL);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void initmap() throws Exception {
        //定位初始化
        mLocationClient = new LocationClient(getApplicationContext());
        mLocationClient.registerLocationListener(new MyLocationListener());
        //获取地图控件引用
        mMapView = (MapView) findViewById(R.id.bmapView);
        mBaiduMap = mMapView.getMap();
        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder().zoom(20).build()));
        //开启地图的定位图层
        mBaiduMap.setMyLocationEnabled(true);
        //通过LocationClientOption设置LocationClient相关参数
        LocationClientOption option = new LocationClientOption();
        option.setOpenGps(true); // 打开gps
        option.setCoorType("bd09ll"); // 设置坐标类型
        option.setIsNeedAddress(true);//设置是否需要地址信息
        option.setScanSpan(1000);
        //设置locationClientOption
        mLocationClient.setLocOption(option);
        //注册LocationListener监听器
        MyLocationListener myLocationListener = new MyLocationListener();
        mLocationClient.registerLocationListener(myLocationListener);
        //注册监听函数
        initOrientation();
        //开启地图定位图层
        mLocationClient.start();
        button();
    }
    @Override
    protected void onResume() {
        super.onResume();
        //在activity执行onResume时执行mMapView. onResume (),实现地图生命周期管理
        mMapView.onResume();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理
        mMapView.onPause();
    }
    @Override
    protected void onDestroy() {
        mLocationClient.stop();
        mBaiduMap.setMyLocationEnabled(false);
        mMapView = null;
        super.onDestroy();
        //在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
        mMapView.onDestroy();
    }
    protected void onStart() {
        super.onStart();
        //开启定位
        mBaiduMap.setMyLocationEnabled(true);
        if (!mLocationClient.isStarted())
            mLocationClient.start();
        //开启方向传感器
        mMyOrientationListener.start();
    }
    @Override
    protected void onStop() {
        super.onStop();
        //停止定位
        mBaiduMap.setMyLocationEnabled(false);
        mLocationClient.stop();
        //停止方向传感器
        mMyOrientationListener.stop();
    }
    //构造地图数据
    public class MyLocationListener extends BDAbstractLocationListener {
        @Override
        public void onReceiveLocation(BDLocation location) {
            //mapView 销毁后不在处理新接收的位置
            if (location == null || mMapView == null){
                return;
            }
            Latitude=location.getLatitude();
            Longitude=location.getLongitude();
            Radius=location.getRadius();
            if(isFirst)
            {
                LatLng latLng = new LatLng(Latitude, Longitude);
                MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng);
                mBaiduMap.animateMapStatus(update);
                MyLocationConfiguration configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL,
                        true, null );
                mBaiduMap.setMyLocationConfiguration(configuration);
                isFirst=false;
            }
            MyLocationData locData = new MyLocationData.Builder()
                    .accuracy(Radius)
                    // 此处设置开发者获取到的方向信息,顺时针0-360
                    .direction(mCurrentX).latitude(Latitude)
                    .longitude(Longitude).build();
            mBaiduMap.setMyLocationData(locData);
        }
    }
    //传感器
    private void initOrientation() {
        //传感器
        mMyOrientationListener = new MyOrientationListener(getApplicationContext());
        mMyOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {
            @Override
            public void onOrientationChanged(float x) {
                mCurrentX = x;
                MyLocationData locData = new MyLocationData.Builder()
                        .accuracy(Radius)
                        // 此处设置开发者获取到的方向信息,顺时针0-360
                        .direction(mCurrentX).latitude(Latitude)
                        .longitude(Longitude).build();
                mBaiduMap.setMyLocationData(locData);
            }
        });
    }
    //按钮响应
    private void button() {
        //按钮
        Button mbut_Loc = (Button) findViewById(R.id.but_Loc);
        //按钮处理
        mbut_Loc.setOnClickListener(this);
    }
    //回到定位点
    private void centerToMyLocation() {
        LatLng latLng = new LatLng(Latitude, Longitude);
        MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(latLng);
        mBaiduMap.animateMapStatus(update);
    }
    @Override
    public void onClick(View v) {
        SDKInitializer.initialize(getApplicationContext());
        switch (v.getId()) {
            case R.id.but_Loc: {
                centerToMyLocation();
                break;
            }
        }
    }
    public void initRadioGroup() {
        //获取radiongroup对象
        mode = (RadioGroup) findViewById(R.id.Mode);
        //通过radiogroup的setoncheckedlistener方法注册监听事件
        //在监听事件中创建oncheckedlistener 在重写oncheckedchanged方法
        mode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                //获取被选中的radiobutton的id
                RadioButton rcheck = (RadioButton) findViewById(checkedId);
                //获取
                String checkText = rcheck.getText().toString();
                MyLocationConfiguration configuration= null;
                switch (checkText)
                {
                    case "跟随态":
                        configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.FOLLOWING,
                                true, null );
                        mBaiduMap.setMyLocationConfiguration(configuration);
                        //罗盘态会改变百度地图的旋转和俯视角度,切换到其他模式需要重置
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).rotate(0).build()));
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).overlook(0).build()));
                        break;
                    case "普通态":
                        configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL,
                                true, null );
                        mBaiduMap.setMyLocationConfiguration(configuration);
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).rotate(0).build()));
                        mBaiduMap.setMapStatus(MapStatusUpdateFactory.newMapStatus(new MapStatus.Builder(mBaiduMap.getMapStatus()).overlook(0).build()));
                        break;
                    case "罗盘态":
                        configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.COMPASS,
                                true, null );
                        mBaiduMap.setMyLocationConfiguration(configuration);
                        break;
                }
            }
        });
    }
}

完整项目
提取码:go1j

;