1. 程式人生 > >android百度地圖開發——地圖sdk之基礎地圖

android百度地圖開發——地圖sdk之基礎地圖

上一節我們介紹了百度定位sdk,這一節我們在定位的基礎上介紹百度地圖sdk。

讓地圖顯示出來

首先把上一節佈局檔案中新增的textview刪掉,然後加入MapView這個控制元件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="com.example.lbstest.MainActivity">
<com.baidu.mapapi.map.MapView android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height
="match_parent" android:clickable="true"/>
</android.support.constraint.ConstraintLayout>

毫無疑問這個控制元件就是顯示地圖的。
接著我們對Activity做以下修改:

    private MapView mMapView;
    private BaiduMap mBaiduMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mlocation = new
LocationClient(getApplicationContext()); mlocation.registerLocationListener(new MyLocationListener()); SDKInitializer.initialize(getApplicationContext()); setContentView(R.layout.activity_main); //初始化方向感測器 myOrientationListener = new MyOrientationListener(this); //獲取地圖控制元件引用 mMapView = (MapView) findViewById(R.id.mapView); mBaiduMap = mMapView.getMap(); //申請許可權 initPermission(); } @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() { super.onDestroy(); mlocation.stop(); //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命週期管理 mMapView.onDestroy(); }

和上一節相比多了SDKInitializer初始化、獲取MapView例項和獲取BaiduMap的例項(BaiduMap是地圖的總控制器,它可以對地圖進行各種各樣的操作),另外還需要重寫onDestroy() 、onPause()、 onResume() 這3個方法對MapView進行管理,以保證資源能夠及時釋放。到這裡地圖已經能夠顯示出來了。

顯示當前位置

聰明的你一定發現我們並沒有設定座標,這樣是不能顯示到我們的位置的, 下面我們讓地圖顯示到我們的位置。
這就需要我們上一節中定位sdk的知識了,首先我們把BDLocationListener做一下修改:

public class MyLocationListener implements BDLocationListener {
        @Override
        public void onReceiveLocation(BDLocation bdLocation) {
            mCurrentAccracy=bdLocation.getRadius();
            mCurrentLantitude=bdLocation.getLatitude();
            mCurrentLongitude=bdLocation.getLongitude();
            if (bdLocation.getLocType() == BDLocation.TypeGpsLocation || bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {
                //設定地圖顯示
                navigateTo(bdLocation);
            }
        }

        @Override
        public void onConnectHotSpotMessage(String s, int i) {

        }
    }

    private void navigateTo(BDLocation location) {
        if (isFirstLocate) {
            //定位座標
            LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            mBaiduMap.animateMapStatus(update);
            //設定縮放級別
            update = MapStatusUpdateFactory.zoomTo(21f);
            mBaiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }
    }

上面的程式碼首先在定位回撥的函式中將經緯度資訊賦值給全域性變數,這是在後面使用的我們暫時不看,然後判斷是否定位成功,成功後呼叫navigateTo()方法,在navigateTo()方法中通過一個boolean全域性變數判斷是否是第一次定位,然戶設定定位座標,縮放級別(其他的設定屬性參照官方文件,這裡就不多介紹了),現在地圖就已經顯示在我們的位置了。

執行效果:
這裡寫圖片描述

顯示定點陣圖標

我們用的地圖都會有一個圖示顯示在我們的位置,下面我們來新增這個圖示。
首先要在onCreate()方法中呼叫BaiduMap的setMyLocationEnabled方法開啟此功能:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mlocation = new LocationClient(getApplicationContext());
        mlocation.registerLocationListener(new MyLocationListener());
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        //初始化方向感測器
        myOrientationListener = new MyOrientationListener(this);
        //獲取地圖控制元件引用
        mMapView = (MapView) findViewById(R.id.mapView);
        mBaiduMap = mMapView.getMap();
        mBaiduMap.setMyLocationEnabled(true);
        //申請許可權
        initPermission();
    }

接著在navigateTo方法中新增MyLocationData的構建邏輯:

    private void navigateTo(BDLocation location) {
        if (isFirstLocate) {
            //定位座標
            LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            mBaiduMap.animateMapStatus(update);
            //設定縮放級別
            update = MapStatusUpdateFactory.zoomTo(21f);
            mBaiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }
        MyLocationData.Builder builder = new MyLocationData.Builder();
                builder.accuracy(mCurrentAccracy);
                builder.latitude(mCurrentLantitude);
                builder.longitude(mCurrentLongitude);
                MyLocationData locationData = builder.build();
                mBaiduMap.setMyLocationData(locationData);
    }

這裡用到了mCurrentAccracy、mCurrentLantitude、mCurrentLongitude這三個儲存了經緯度資訊的全域性變數,MyLocationConfiguration 的最後一個引數設定圖示的樣式,傳入空為預設圖示,當然也可以傳入一個BitmapDescriptor設定自定義圖示。
最後在onDestroy()方法中關閉此功能:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mlocation.stop();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命週期管理
        mMapView.onDestroy();
        mBaiduMap.setMyLocationEnabled(false);
    }

到這裡顯示定點陣圖標就完成了。

執行效果:
這裡寫圖片描述

定點陣圖標與感測器結合顯示方向

雖然當前位置的圖示顯示出來了,但是對於一些路痴來說(比如我)仍然找不到方向,這就需要顯示方向了,顯示方向的功能實現需要手機的加速度和地磁場感測器,根據這兩個感測器計算出手機的角度,然後將這個角度傳給MyLocationData.Builder的builder.direction()方法用於設定方向。下面我們開始實現這一功能。
在往下看之前建議先看一下一篇關於感測器的文章:http://www.2cto.com/kf/201412/359292.html

首先建立一個類MyOrientationListener,所有感測器的操作都封裝在這個類中:

public class MyOrientationListener implements SensorEventListener {

    private Context context;
    private SensorManager sensorManager;
    private Sensor accelerometer; // 加速度感測器
    private Sensor magnetic; // 地磁場感測器
    private float[] accelerometerValues = new float[3];
    private float[] magneticFieldValues = new float[3];
    private float lastX;

    private OnOrientationListener onOrientationListener ;

    public MyOrientationListener(Context context)
    {
        this.context = context;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // 接受方向感應器的型別
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            accelerometerValues = event.values;
        }
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            magneticFieldValues = event.values;
        }
        getOrientation();
    }

    public void getOrientation() {
        float[] values = new float[3];
        float[] R = new float[9];
        SensorManager.getRotationMatrix(R, null, accelerometerValues, magneticFieldValues);
        //將角度資訊計算後返回到values中
        SensorManager.getOrientation(R, values);
        values[0] = (float) Math.toDegrees(values[0]);
        //當方向的改變大於一度時回撥監聽
        if( Math.abs(values[0]- lastX) > 1.0 )
        {
            onOrientationListener.onOrientationChanged(values[0]);
        }
        lastX = values[0] ;
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    public void start(){
        // 獲得感測器管理器
        sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        if (sensorManager != null)
        {
            // 初始化加速度感測器
            accelerometer = sensorManager
                    .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            // 初始化地磁場感測器
            magnetic = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        }
        // 註冊
        if (accelerometer != null || magnetic!=null)
        {
            sensorManager.registerListener(this,
                    accelerometer, Sensor.TYPE_ACCELEROMETER);
            sensorManager.registerListener(this, magnetic,
                    Sensor.TYPE_MAGNETIC_FIELD);
        }
    }

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

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

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

如果你看了我推薦的那篇文章上面的程式碼對你來說就是小菜一碟,註釋也寫得清楚我就不詳細介紹了,使用時只需建立一個例項,然後呼叫start()方法,在設定監聽就可以了,最後別忘了stop()。
我們在我們Activity實現這個類:
首先在oncreat()方法中例項化這個類,並設定監聽,把接收的引數賦值給一個全域性變數:

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mlocation = new LocationClient(getApplicationContext());
        mlocation.registerLocationListener(new MyLocationListener());
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        //初始化方向感測器
        myOrientationListener = new MyOrientationListener(this);
        myOrientationListener.setOnOrientationListener(this);
        //獲取地圖控制元件引用
        mMapView = (MapView) findViewById(R.id.mapView);
        mBaiduMap = mMapView.getMap();
        mBaiduMap.setMyLocationEnabled(true);
        //申請許可權
        initPermission();
    }

接著在onResume()方法中呼叫MyOrientationListener的start()方法:

    @Override
    protected void onResume() {
        super.onResume();
        //在activity執行onResume時執行mMapView. onResume (),實現地圖生命週期管理
        mMapView.onResume();
        myOrientationListener.start();
    }

然後把navigateTo()方法中對MyLocationData.Builder的配置刪掉:

 private void navigateTo(BDLocation location) {
        if (isFirstLocate) {
            //定位座標
            LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            mBaiduMap.animateMapStatus(update);
            //設定縮放級別
            update = MapStatusUpdateFactory.zoomTo(21f);
            mBaiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }
    }

最後在MyOrientationListener的監聽方法中新增以下程式碼:

    @Override
    public void onOrientationChanged(float x) {
        mXDirection = (int) x;
        builder = new MyLocationData.Builder();
        builder.accuracy(mCurrentAccracy);
        builder.direction(mXDirection);
        builder.latitude(mCurrentLantitude);
        builder.longitude(mCurrentLongitude);
        MyLocationData locationData = builder.build();
        mBaiduMap.setMyLocationData(locationData);
        // 設定自定義圖示
        //BitmapDescriptor mCurrentMarker = BitmapDescriptorFactory.fromResource(R.drawable.arrow);
        MyLocationConfiguration configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, null);
        mBaiduMap.setMyLocationConfiguration(configuration);
    }

我們對MyLocationData增加了一個設定builder.direction(mXDirection)。最後還要用MyLocationConfiguration對BaiduMap進行配置,MyLocationConfiguration的第一個引數為圖示顯示模式,有三種模式我就不一一介紹了,有興趣的同學可以自己試試,或者參考官方文件,第二個引數表示是否顯示方向,第三個引數為自定義圖示,如果使用預設圖示傳入空就行了。

最後別忘了在onDestroy()方法中stop方向感測器:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        mlocation.stop();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命週期管理
        mMapView.onDestroy();
        mBaiduMap.setMyLocationEnabled(false);
    }

執行效果:
這裡寫圖片描述

到這裡百度地圖sdk的初使用也完成了,下面是完整的程式碼:

public class MainActivity extends AppCompatActivity implements MyOrientationListener.OnOrientationListener{

    private MapView mMapView;
    private BaiduMap mBaiduMap;
    private List<String> permissionList;

    private Boolean isFirstLocate=true;
    private float mCurrentAccracy;
    private double mCurrentLantitude;
    private double mCurrentLongitude;
    private int mXDirection;
    public LocationClient mlocation;
    private MyLocationData.Builder builder;
    private MyOrientationListener myOrientationListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mlocation = new LocationClient(getApplicationContext());
        mlocation.registerLocationListener(new MyLocationListener());
        SDKInitializer.initialize(getApplicationContext());
        setContentView(R.layout.activity_main);
        //初始化方向感測器
        myOrientationListener = new MyOrientationListener(this);
        myOrientationListener.setOnOrientationListener(this);
        //獲取地圖控制元件引用
        mMapView = (MapView) findViewById(R.id.mapView);
        mBaiduMap = mMapView.getMap();
        mBaiduMap.setMyLocationEnabled(true);
        //申請許可權
        initPermission();
    }

    private void initPermission() {
        permissionList = new ArrayList<>();
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            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()]);
            ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
        } else {
            //請求定位
            requestLocation();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 1:
                if (grantResults.length > 0) {
                    for (int result : grantResults) {
                        if (result != PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(MainActivity.this, "必須同意所有許可權", Toast.LENGTH_LONG).show();
                            finish();
                            return;
                        }
                    }
                    //請求定位
                    requestLocation();
                } else {
                    finish();
                }
                break;
        }
    }

    private void requestLocation() {
        //設定定位屬性
        initLocation();
        //開始定位
        mlocation.start();
    }

    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        //設定百度座標
        option.setCoorType("bd09ll");
        option.setOpenGps(true); // 開啟gps
        //設定定位延遲
        option.setScanSpan(3000);
        mlocation.setLocOption(option);
    }

    @Override
    public void onOrientationChanged(float x) {
        mXDirection = (int) x;
        builder = new MyLocationData.Builder();
        builder.accuracy(mCurrentAccracy);
        builder.direction(mXDirection);
        builder.latitude(mCurrentLantitude);
        builder.longitude(mCurrentLongitude);
        MyLocationData locationData = builder.build();
        mBaiduMap.setMyLocationData(locationData);
        // 設定自定義圖示
        //BitmapDescriptor mCurrentMarker = BitmapDescriptorFactory.fromResource(R.drawable.arrow);
        MyLocationConfiguration configuration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, true, null);
        mBaiduMap.setMyLocationConfiguration(configuration);
    }


    public class MyLocationListener implements BDLocationListener {
        @Override
        public void onReceiveLocation(BDLocation bdLocation) {
            mCurrentAccracy=bdLocation.getRadius();
            mCurrentLantitude=bdLocation.getLatitude();
            mCurrentLongitude=bdLocation.getLongitude();
            if (bdLocation.getLocType() == BDLocation.TypeGpsLocation || bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {
                //設定地圖顯示
                navigateTo(bdLocation);
            }
        }

        @Override
        public void onConnectHotSpotMessage(String s, int i) {

        }
    }

    private void navigateTo(BDLocation location) {
        if (isFirstLocate) {
            //定位座標
            LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
            MapStatusUpdate update = MapStatusUpdateFactory.newLatLng(ll);
            mBaiduMap.animateMapStatus(update);
            //設定縮放級別
            update = MapStatusUpdateFactory.zoomTo(21f);
            mBaiduMap.animateMapStatus(update);
            isFirstLocate = false;
        }

    }


    @Override
    protected void onResume() {
        super.onResume();
        //在activity執行onResume時執行mMapView. onResume (),實現地圖生命週期管理
        mMapView.onResume();
        myOrientationListener.start();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //在activity執行onPause時執行mMapView. onPause (),實現地圖生命週期管理
        mMapView.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        myOrientationListener.stop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mlocation.stop();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命週期管理
        mMapView.onDestroy();
        mBaiduMap.setMyLocationEnabled(false);
    }
}

public class MyOrientationListener implements SensorEventListener {

    private Context context;
    private SensorManager sensorManager;
    private Sensor accelerometer; // 加速度感測器
    private Sensor magnetic; // 地磁場感測器
    private float[] accelerometerValues = new float[3];
    private float[] magneticFieldValues = new float[3];
    private float lastX;

    private OnOrientationListener onOrientationListener ;

    public MyOrientationListener(Context context)
    {
        this.context = context;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // 接受方向感應器的型別
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            accelerometerValues = event.values;
        }
        if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
            magneticFieldValues = event.values;
        }
        getOrientation();
    }

    public void getOrientation() {
        float[] values = new float[3];
        float[] R = new float[9];
        SensorManager.getRotationMatrix(R, null, accelerometerValues, magneticFieldValues);
        //將角度資訊計算後返回到values中
        SensorManager.getOrientation(R, values);
        values[0] = (float) Math.toDegrees(values[0]);
        //當方向的改變大於一度時回撥監聽
        if( Math.abs(values[0]- lastX) > 1.0 )
        {
            onOrientationListener.onOrientationChanged(values[0]);
        }
        lastX = values[0] ;
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    public void start(){
        // 獲得感測器管理器
        sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        if (sensorManager != null)
        {
            // 初始化加速度感測器
            accelerometer = sensorManager
                    .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
            // 初始化地磁場感測器
            magnetic = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        }
        // 註冊
        if (accelerometer != null || magnetic!=null)
        {
            sensorManager.registerListener(this,
                    accelerometer, Sensor.TYPE_ACCELEROMETER);
            sensorManager.registerListener(this, magnetic,
                    Sensor.TYPE_MAGNETIC_FIELD);
        }
    }

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

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

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