Android开发 百度地图开发(定位、传感器应用)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhaokx3/article/details/53563574

前言

结合传感器的使用实现百度地图的开发,基本功能包括实时定位。其中利用传感器确定手机朝向并实现图标方位转动以及摇一摇功能的实现。

功能介绍

  1. 调用百度地图SDK实现地图的展示以及交互,其中包括左下角的按钮,实现的功能如下:

  2. 获取当前所在位置与方向,并在百度地图上显示出来,如图中黑色箭头所示,箭头中心位置为定位结果,箭头方向为手机的朝向。

  3. 实现简单摇一摇功能,比如我通过摇一摇实现了手机的震动以及更多模式的选择。
  4. 最终界面显示:

功能实现

大坑说在前面:按一般习惯,控件的声明后就直接显示了,所以一般将它们在一起。比如在一个函数里设置定位图标并紧接着放入地图,但在传感器监听器中直接调用该函数时发现,图标一直存在明显的闪动,后分析觉得是每次重复声明并设置图标导致的。无独有偶,在实现摇一摇功能时将创建对话框(Create)和显示(show)一起直接放在摇一摇检测中时,运行也会导致对话框无法消失,发现也是不断创建对话框导致的,所以将create与show分开,即在检测外面单独定义一个创建函数(createDialog),检测通过后只是简单的显示dialog,详见后面。如有错误欢迎指正,谢谢。

前期配置

要想完成百度地图的开发,需要成为其开发者,过程包括申请密钥,下载SDK依赖包,AS导入包等,具体参见百度地图开发者官方网站基本可以完成,这里就不细讲了,有疑问可以沟通:
申请密钥Android SDK http://lbsyun.baidu.com/index.php?title=androidsdk/guide/key
配置环境及发布Android SDK :http://lbsyun.baidu.com/index.php?title=androidsdk/guide/buildproject
Hello BaiduMap:http://lbsyun.baidu.com/index.php?title=androidsdk/guide/hellobaidumap

动态申请权限

特别的,由于Android API 23以上需要动态申请权限,这里使用一个Github上的开源工具RxAndroid4进行处理,也可以自己根据Android官方的API进行或者其他的工具。首先需要在build.gradle中加入需要的依赖项:

然后新建一个启动的Activity,在这个单独的Activity的onCreate方法里检查是否获取需要的权限,如果没有则直接退出程序,实现代码参考如下:

实时定位

获取用户的经纬度信息需要使用位置管理器LocationManager,同时需要确认手机打开了位置服务。

mLocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

位置服务有一个位置的数据来源称为provider,可以分为NETWORK_PROVIDER和GPS_PROVIDER,其中NETWORK_PROVIDER使用基站和Wi-Fi信号来决定用户的位置,更新速度更快耗电量更少,但可能精确度较低,而GPS_PROVIDER使用GPS卫星进行定位,精度高但只能在室外使用,耗电量也更高。在使用过程中可以选择使用其中一个provider,也可以两个provider同时使用。(我的实现为获取最佳provider)
位置检测更新与监听器实现:

locationManager.requestLocationUpdates(provider, 0, 0, locationListener);


private LocationListener locationListener = new LocationListener() {

        @Override
        public void onLocationChanged(Location location) {
            convertCoor(location);
            showLocation();
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {
            //location = getBestLocation(locationManager);
            location = locationManager.getLastKnownLocation(provider);
        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    };
通过获取最佳provider获取位置坐标(定位的关键)
location = locationManager.getLastKnownLocation(provider);


private void getBestProvider() {
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_FINE);//高精度
        criteria.setAltitudeRequired(false);//无海拔要求
        criteria.setBearingRequired(false);//无方位要求
        criteria.setCostAllowed(true);//允许产生资费

        // 获取最佳服务对象
        provider = locationManager.getBestProvider(criteria,true);
    }
由于百度地图所用的位置坐标标准不同于直接获取的位置坐标,所以需要一个坐标的转换:
private void convertCoor(Location location) {
        // 将GPS设备采集的原始GPS坐标转换成百度坐标
        CoordinateConverter converter  = new CoordinateConverter();
        converter.from(CoordinateConverter.CoordType.GPS);
        converter.coord(new LatLng(location.getLatitude(), location.getLongitude()));
        desLatLng = converter.convert();
    }
最后就是添加定位图标及显示位置:
private void init() {

        Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.pointer), 100, 100, true);
        BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);

        baiduMap.setMyLocationEnabled(true);
        MyLocationConfiguration configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, bitmapDescriptor);
        baiduMap.setMyLocationConfigeration(configuration);

        if (isFirst) {
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(desLatLng);
            baiduMap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomBy(5f);
            baiduMap.animateMapStatus(update);

            isFirst = false;
            createDialog();
        }
    }

    private void showLocation() {

        if (desLatLng != null) {
            MyLocationData.Builder data = new MyLocationData.Builder();
            data.latitude(desLatLng.latitude);
            data.longitude(desLatLng.longitude);
            data.direction(currentRotation);
            baiduMap.setMyLocationData(data.build());
        }
    }

计算手机朝向

计算手机朝向需要用到加速度传感器和地磁传感器,类似于位置服务的实现,传感器的使用也需要传感器管理器来管理加速度传感器和地磁传感器,实现如下:

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
magneticSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
private SensorEventListener sensorEventListener = new SensorEventListener() {
        float[] accelerometerValues = new float[3];
        float[] maneticValues = new float[3];
        @Override
        public void onSensorChanged(SensorEvent sensorEvent) {

            // 计算方向角
            if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                accelerometerValues = sensorEvent.values.clone();
            } else if (sensorEvent.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
                maneticValues = sensorEvent.values.clone();
            }
            float[] R = new float[9];
            float[] values = new float[3];
            // 第一个是方向角度参数,第二个参数是倾斜角度参数
            SensorManager.getRotationMatrix(R, null, accelerometerValues, maneticValues);
            SensorManager.getOrientation(R, values);
            currentRotation = (float) Math.toDegrees(values[0]);
            showLocation();
}

ToggleButton的实现

  • 布局文件
<ToggleButton
        android:id="@+id/toggle_center"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="20dp"
        android:layout_marginLeft="20dp"
        android:background="@drawable/toggle_center"
        android:checked="true"
        android:textOff=""
        android:textOn=""/>
  • ToggleButton的监听
toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    locationManager.requestLocationUpdates(provider, 0, 0, locationListener);
                    MapStatus mapStatus = new MapStatus.Builder().target(desLatLng).build();
                    MapStatusUpdate mapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mapStatus);
                    baiduMap.setMapStatus(mapStatusUpdate);
                } else {
                    showLocation();
                }
            }
        });

摇一摇功能

  1. 摇一摇的检测需要计算加速传感器在x,y,z三个方向的值,并设置一个临界值来到达检测的目的(亲测设置15有点敏感),具体实现如下:
// 摇一摇功能
            float xValue = Math.abs(accelerometerValues[0]);
            float yValue = Math.abs(accelerometerValues[1]);
            float zvalue =  Math.abs(accelerometerValues[2]);

            if (xValue > 15||yValue > 15||zvalue > 15){
                //Toast.makeText(MainActivity.this,"摇一摇",Toast.LENGTH_SHORT).show();
                dialog.show();
                //摇动手机后,伴随震动提示
                vibrator.vibrate(500);
            }
  1. 实现摇一摇的震动效果
    • 首先需要在Manifest中添加震动的权限:
// 震动权限
<uses-permission  android:name="android.permission.VIBRATE"/>
  • 接着,打开震动服务并添加摇一摇的震动效果:
vibrator = (Vibrator) getSystemService(this.VIBRATOR_SERVICE);


vibrator.vibrate(500);
  1. 更多模式选择
    通过摇一摇弹出列表对话框,从而达到选择地图模式的盲目地,模式选择包括普通模式,卫星模式,路况图和热力显示四个选择,具体实现如下:
    首先,创建一个列表对话框,提供四种选择:
private  void createDialog() {
        LayoutInflater layoutInflater = LayoutInflater.from(MainActivity.this);
        View mTitleView = layoutInflater.inflate(R.layout.title, null);

        final String[] modes = {"普通模式", "卫星模式", "路况模式","热力模式"};
        dialog = new AlertDialog.Builder(MainActivity.this)
                .setCustomTitle(mTitleView)
                .setItems(modes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        switch (which) {
                            case 0:
                                baiduMap.setTrafficEnabled(false);
                                baiduMap.setBaiduHeatMapEnabled(false);
                                baiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
                                break;
                            case 1:
                                baiduMap.setTrafficEnabled(false);
                                baiduMap.setBaiduHeatMapEnabled(false);
                                baiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
                                break;
                            case 2:
                                baiduMap.setTrafficEnabled(true);
                                break;
                            case 3:
                                baiduMap.setBaiduHeatMapEnabled(true);
                                break;
                            default:
                                break;
                        }
                    }
                }).create();
    }

当检测到摇动时,显示模式列表选择对话框:

if (xValue > 15||yValue > 15||zvalue > 15){
                //Toast.makeText(MainActivity.this,"摇一摇",Toast.LENGTH_SHORT).show();
                dialog.show();
                //摇动手机后,伴随震动提示
                vibrator.vibrate(500);
            }

说明:该对话框的标题栏为自定义的,即使用setCustomTitle设置标题栏而不是直接setTitle。

附上MainActivity代码

package 包名;

import android.Manifest;
import android.app.Dialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Vibrator;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

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.MapStatus;
import com.baidu.mapapi.map.MapStatusUpdate;
import com.baidu.mapapi.map.MapStatusUpdateFactory;
import com.baidu.mapapi.map.MyLocationConfiguration;
import com.baidu.mapapi.map.MyLocationData;
import com.baidu.mapapi.map.TextureMapView;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.utils.CoordinateConverter;

public class MainActivity extends AppCompatActivity {

    private TextureMapView mMapView = null;
    private ToggleButton toggleButton;
    private TextView showInfo = null;

    private SensorManager sensorManager;
    private Sensor magneticSensor;
    private Sensor accelerometerSensor;
    private Vibrator vibrator = null;
    private Dialog dialog;
    private float currentRotation;

    private LocationManager locationManager;
    private Location location;
    private String provider;
    private LatLng desLatLng;
    private BaiduMap baiduMap;
    private boolean isFirst = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //在使用SDK各组件之前初始化context信息,传入ApplicationContext
        //注意该方法要再setContentView方法之前实现
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        //获取地图控件引用
        mMapView = (TextureMapView) findViewById(R.id.bmapView);
        toggleButton = (ToggleButton) findViewById(R.id.toggle_center);
        showInfo = (TextView) findViewById(R.id.showInfo);

        // 获取baiduMap对象
        baiduMap = mMapView.getMap();
        // 设置可改变地图位置
        baiduMap.setMyLocationEnabled(true);

        locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); // 获取位置服务
        // 获取最好的provider
        getBestProvider();
        if (locationManager.isProviderEnabled(provider)) {
            location = locationManager.getLastKnownLocation(provider);
            if (location != null) {
                convertCoor(location);
            }
            locationManager.requestLocationUpdates(provider, 0, 0, locationListener);
        }

        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        通过摇一摇弹出列表对话框,从而达到选择地图模式的盲目地,模式选择包括普通模式,卫星模式,路况图和热力显示四个选择,具体实现如下:
        首先,创建一个列表对话框,提供四种选择:

        magneticSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        init();
        showLocation();

        baiduMap.setOnMapTouchListener(new BaiduMap.OnMapTouchListener() {
            @Override
            public void onTouch(MotionEvent motionEvent) {
                switch (motionEvent.getAction()) {
                    case MotionEvent.ACTION_MOVE:
                        toggleButton.setChecked(false);
                        break;
                    default:
                        break;
                }
            }
        });

        toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    locationManager.requestLocationUpdates(provider, 0, 0, locationListener);
                    MapStatus mapStatus = new MapStatus.Builder().target(desLatLng).build();
                    MapStatusUpdate mapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mapStatus);
                    baiduMap.setMapStatus(mapStatusUpdate);
                } else {
                    showLocation();
                }
            }
        });
    }

    private void getBestProvider() {
        Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_FINE);//高精度
        criteria.setAltitudeRequired(false);//无海拔要求
        criteria.setBearingRequired(false);//无方位要求
        criteria.setCostAllowed(true);//允许产生资费

        // 获取最佳服务对象
        provider = locationManager.getBestProvider(criteria,true);
    }

    private void convertCoor(Location location) {
        // 将GPS设备采集的原始GPS坐标转换成百度坐标
        CoordinateConverter converter  = new CoordinateConverter();
        converter.from(CoordinateConverter.CoordType.GPS);
        converter.coord(new LatLng(location.getLatitude(), location.getLongitude()));
        desLatLng = converter.convert();
    }

    private void init() {

        Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.pointer), 100, 100, true);
        BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);

        baiduMap.setMyLocationEnabled(true);
        MyLocationConfiguration configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, bitmapDescriptor);
        baiduMap.setMyLocationConfigeration(configuration);

        if (isFirst) {
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(desLatLng);
            baiduMap.animateMapStatus(update);
            update = MapStatusUpdateFactory.zoomBy(5f);
            baiduMap.animateMapStatus(update);

            isFirst = false;
            createDialog();
        }
    }

    private void showLocation() {

        if (desLatLng != null) {
            MyLocationData.Builder data = new MyLocationData.Builder();
            data.latitude(desLatLng.latitude);
            data.longitude(desLatLng.longitude);
            data.direction(currentRotation);
            baiduMap.setMyLocationData(data.build());
        }
    }

    private LocationListener locationListener = new LocationListener() {

        @Override
        public void onLocationChanged(Location location) {
            convertCoor(location);
            showLocation();
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {
            location = locationManager.getLastKnownLocation(provider);
        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    };

    private SensorEventListener sensorEventListener = new SensorEventListener() {
        float[] accelerometerValues = new float[3];
        float[] maneticValues = new float[3];
        @Override
        public void onSensorChanged(SensorEvent sensorEvent) {

            // 计算方向角
            if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                accelerometerValues = sensorEvent.values.clone();
            } else if (sensorEvent.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
                maneticValues = sensorEvent.values.clone();
            }
            float[] R = new float[9];
            float[] values = new float[3];
            // 第一个是方向角度参数,第二个参数是倾斜角度参数
            SensorManager.getRotationMatrix(R, null, accelerometerValues, maneticValues);
            SensorManager.getOrientation(R, values);
            currentRotation = (float) Math.toDegrees(values[0]);
            showLocation();

            // 摇一摇功能
            float xValue = Math.abs(accelerometerValues[0]);
            float yValue = Math.abs(accelerometerValues[1]);
            float zvalue =  Math.abs(accelerometerValues[2]);

            if (xValue > 15||yValue > 15||zvalue > 15){
                //Toast.makeText(MainActivity.this,"摇一摇",Toast.LENGTH_SHORT).show();
                dialog.show();
                //摇动手机后,伴随震动提示
                vibrator.vibrate(500);
            }
        }
        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {}
    };

    private  void createDialog() {
        LayoutInflater layoutInflater = LayoutInflater.from(MainActivity.this);
        View mTitleView = layoutInflater.inflate(R.layout.title, null);

        final String[] modes = {"普通模式", "卫星模式", "路况模式","热力模式"};
        dialog = new AlertDialog.Builder(MainActivity.this)
                .setCustomTitle(mTitleView)
                .setItems(modes, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        switch (which) {
                            case 0:
                                baiduMap.setTrafficEnabled(false);
                                baiduMap.setBaiduHeatMapEnabled(false);
                                baiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
                                break;
                            case 1:
                                baiduMap.setTrafficEnabled(false);
                                baiduMap.setBaiduHeatMapEnabled(false);
                                baiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
                                break;
                            case 2:
                                baiduMap.setTrafficEnabled(true);
                                break;
                            case 3:
                                baiduMap.setBaiduHeatMapEnabled(true);
                                break;
                            default:
                                break;
                        }
                    }
                }).create();
    }

    @Override
    protected void onStart() {
        super.onStart();
        //开启定位
        baiduMap.setMyLocationEnabled(true);
    }


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

        sensorManager.registerListener(sensorEventListener, magneticSensor, SensorManager.SENSOR_DELAY_GAME);
        sensorManager.registerListener(sensorEventListener, accelerometerSensor, SensorManager.SENSOR_DELAY_GAME);
    }
    @Override
    protected void onPause() {
        super.onPause();
        locationManager.removeUpdates(locationListener);
        //在activity执行onPause时执行mMapView. onPause (),实现地图生命周期管理
        mMapView.onPause();
        sensorManager.unregisterListener(sensorEventListener);
    }
}

至此,基于百度地图的开发主要内容基本完成,整个简单app的实现也基本能完成了。如需整个项目的代码可留下邮箱。

猜你喜欢

转载自blog.csdn.net/zhaokx3/article/details/53563574