Android開發百度地圖應用——實現定位功能


本隨筆是為了記錄課程中的一個安卓開發作業:做一個安卓端的地圖應用,要求可以實現地圖定位功能,輸入一個地址后,可以在地圖上定位到這個地址並給出經緯度(使用 Android Studio 開發)

項目已托管地址:https://github.com/yunkailiu/Mybaidumap

1.在百度地圖開放平台新建項目:

  應用名稱自己確定,類型選擇Android SDK,啟用的服務都按照默認的勾選即可,最重要的是兩個SHA1的確定,按照官方文檔的說明只填寫開發版的即可,但創建的時候發布版是必填項(官方文檔應該更新了。。。。),因此這里兩個版本全部填寫為開發版本的SHA1,若后續發布則再回來修改發布版的即可(具體操作可以參考官方文檔)。

 

 

  按照官方說明獲取SHA1,這里使用開發版,進入.android目錄下輸入指令:

 

  獲取包名:

  按照以上步驟填寫完之后會按照生成該項目的密鑰,后續開發會使用:

2.對Android Studio進行相關配置:

  首先在百度地圖開發平台下載會用到的百度開發包:

 

   下載解壓之后,將里面的 BaiduLBS_Android.jar包導入項目的libs文件夾下,右鍵-選擇Add As Library,導入到工程中,在src/main/目錄下新建jniLibs目錄,將剩下的幾個文件夾導入到該目錄。

3.項目開發:

  做好前面的配置工作之后就可以開始寫我們的代碼了,在安卓開發中要在不同的項目文件中做各自的工作,具體可參考官方給出的Hello BaiduMap實例。

  (1)在AndroidManifest.xml中添加本項目需要用到的權限、申請的密鑰:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.mymap">
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    //獲取設備網絡狀態,禁用后無法獲取網絡狀態
    <uses-permission android:name="android.permission.INTERNET"/>
    //網絡權限,當禁用后,無法進行檢索等相關業務
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    //讀取設備硬件信息,統計數據
    <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
    //讀取系統信息,包含系統版本等信息,用作統計
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    //獲取設備的網絡狀態,鑒權所需網絡代理
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    //允許sd卡寫權限,需寫入地圖數據,禁用后無法顯示地圖
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    //獲取統計數據
    <uses-permission android:name="android.permission.CAMERA" />
    //使用步行AR導航,配置Camera權限
    <!-- 訪問精確位置的權限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- 這個權限用於進行網絡定位-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>


    <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">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- 聲明service組件 -->
        <service
            android:name="com.baidu.location.f"
            android:enabled="true"
            android:process=":remote" >
        </service>
        <meta-data
            android:name="com.baidu.lbsapi.API_KEY"
            android:value="剛才申請好的密鑰" />
    </application>

</manifest>

  (2)在activity_main.xml主界面布局文件中設置布局,因為我是通過新增菜單欄實現兩個功能,因此在完成該布局前先在res中新建menu文件夾,新建menu_main.xml文件來插入菜單功能控件:

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

    <item
        android:id="@+id/menu_item_mylocation"
        android:title="我的位置"
        app:showAsAction="never" />
    <item
        android:id="@+id/menu_item_sitesearch"
        android:title="地址搜索"
        app:showAsAction="never" />
</menu>

  (3)在activity_main.xml中寫入控件代碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal"
        android:id="@+id/linearLayout2"
        android:visibility="gone"
        >
        <EditText
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:hint="地址"
            android:id="@+id/editText_site" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:hint="前往"
            android:id="@+id/button_sitesearch"/>
    </LinearLayout>

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

  (4)MainActivity主文件內容:

package com.example.mymap;

import android.content.Context;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
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.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
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 {
    private MapView mMapView = null;//地圖控件
    private BaiduMap mBaiduMap;//百度地圖對象
    private Context context;
    //實現定位相關數據類型
    private LocationClient mLocationClient;//定位服務客戶對象
    private MyLocationListener myLocationListener;//重寫的監聽類
    private boolean isFirstIn = true;
    private double mLatitude;//存儲自己的緯度
    private double mLongitude;//存儲自己的經度
    private float myCurrentX;

    private BitmapDescriptor myIconLocation1;//當前位置的箭頭圖標
    private MyOrientationListener myOrientationListener;//方向感應器類對象
    private MyLocationConfiguration.LocationMode locationMode;//定位圖層顯示方式
    private LinearLayout myLinearLayout2; //地址搜索區域

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

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        SDKInitializer.initialize(getApplicationContext());
        //自4.3.0起,百度地圖SDK所有接口均支持百度坐標和國測局坐標,用此方法設置您使用的坐標類型.
        //包括BD09LL和GCJ02兩種坐標,默認是BD09LL坐標。
        setContentView(R.layout.activity_main);
        this.context = this;
        initView();
        //初始化定位
        initLocation();
    }

    private void initLocation() {
        locationMode = MyLocationConfiguration.LocationMode.NORMAL;
        //客戶端的定位服務
        mLocationClient = new LocationClient(this);
        myLocationListener = new MyLocationListener();
        //注冊監聽器
        mLocationClient.registerLocationListener(myLocationListener);
        //設置定位參數
        LocationClientOption option = new LocationClientOption();
        //設置坐標類型
        option.setCoorType("bd09ll");
        //設置是否需要地址信息,默認為無地址
        option.setIsNeedAddress(true);
        //設置是否打開gps進行定位
        option.setOpenGps(true);
        //設置掃描間隔為1秒
        option.setScanSpan(1000);
        //傳入設置好的信息
        mLocationClient.setLocOption(option);

        //初始化圖標,BitmapDescriptorFactory是bitmap描述信息工廠類
        myIconLocation1 = BitmapDescriptorFactory.fromResource(R.drawable.location_marker);
        //配置定義的圖層,使之生效
        MyLocationConfiguration configuration = new MyLocationConfiguration(locationMode,true,myIconLocation1);
        mBaiduMap.setMyLocationConfiguration(configuration);

        myOrientationListener = new MyOrientationListener(context);
        //接口回調來實現實時方向的改變
        myOrientationListener.setOnOrientationListener(new MyOrientationListener.OnOrientationListener() {
            @Override
            public void onOrientationChanged(float x) {
                myCurrentX = x;
            }
        });
    }

    private void initView() {
        //獲取地圖控件引用
        mMapView = (MapView) findViewById(R.id.bmapView);
        //設置地圖放大比例
        mBaiduMap = mMapView.getMap();
        MapStatusUpdate msu = MapStatusUpdateFactory.zoomTo(15.0f);
        mBaiduMap.setMapStatus(msu);
    }

    /**
     * 創建菜單操作
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        getMenuInflater().inflate(R.menu.menu_main,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item){
        switch (item.getItemId())
        {
            /**
             * 返回自己所在位置
             */
            case R.id.menu_item_mylocation:
                getLocationByLL(mLatitude,mLongitude);
            break;
            /**
             * 根據地址名前往所在的位置
             */
            case R.id.menu_item_sitesearch:
                myLinearLayout2 = (LinearLayout)findViewById(R.id.linearLayout2);
                //顯示地址搜索區域
                myLinearLayout2.setVisibility(View.VISIBLE);
                final EditText myEditText_site = (EditText) findViewById(R.id.editText_site);
                Button button_site = (Button) findViewById(R.id.button_sitesearch);

                button_site.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        final String site_str = myEditText_site.getText().toString();
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                AddressToLatitudeLongitude at = new AddressToLatitudeLongitude(site_str);
                                at.getLatAndLngByAddress();
                                Looper.prepare();
                                getLocationByLL(at.getLatitude(),at.getLongitude());
                                Looper.loop();
                            }
                        }).start();
                        //隱藏前面地址輸入區域
                        myLinearLayout2.setVisibility(View.GONE);
                        //隱藏輸入法鍵盤
                        InputMethodManager imm = (InputMethodManager)getSystemService(
                                Context.INPUT_METHOD_SERVICE);
                        imm.hideSoftInputFromWindow(v.getWindowToken(),0);

                    }
                });
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * 根據經緯度返回當前位置
     * @param la
     * @param lg
     */
    private void getLocationByLL(double la, double lg) {
        LatLng latLng = new LatLng(la,lg);
        //描述地圖狀態將要發生的變化,通過當前經緯度來使地圖顯示到該位置
        MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);
        mBaiduMap.setMapStatus(msu);
        getLatAndLng(la,lg);
    }

    /**
     * 返回當前位置的經緯度,並以閃現消息的方式顯示
     */
    private void getLatAndLng(double la, double lg){
        String latAndlng = "經度:"+ String.valueOf(lg) + "\n" + "緯度:" + String.valueOf(la);
        Toast.makeText(context,latAndlng,Toast.LENGTH_LONG).show();
    }


    @Override
    protected void onStart() {

        super.onStart();
        //開啟定位
        mBaiduMap.setMyLocationEnabled(true);
        if(!mLocationClient.isStarted()){
            mLocationClient.start();
        }
        myOrientationListener.start();
    }
    @Override
    protected void onStop() {
        super.onStop();
        //停止定位
        mBaiduMap.setMyLocationEnabled(false);
        mLocationClient.stop();
        myOrientationListener.stop();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //在activity執行onDestroy時執行mMapView.onDestroy(),實現地圖生命周期管理
        mMapView.onDestroy();
    }
    @Override
    protected void onResume() {
        super.onResume();
        //在activity執行onResume時執行mMapView. onResume (),實現地圖生命周期管理
        mMapView.onResume();
    }
    @Override
    protected void onPause() {
        super.onPause();
        //在activity執行onPause時執行mMapView. onPause (),實現地圖生命周期管理
        mMapView.onPause();
    }

    private class MyLocationListener implements BDLocationListener{

        @Override
        public void onReceiveLocation(BDLocation location) {
            mLatitude = location.getLatitude();
            mLongitude = location.getLongitude();
            MyLocationData data = new MyLocationData.Builder()//
                    .direction(myCurrentX)//
                    .accuracy(location.getRadius())//
                    .latitude(mLatitude)//
                    .longitude(mLongitude).build();
            mBaiduMap.setMyLocationData(data);
            if (isFirstIn){
                /*LatLng latLng = new LatLng(location.getLatitude(),location.getLongitude());
                MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(latLng);
                mBaiduMap.animateMapStatus(msu);*/
                getLocationByLL(mLatitude,mLongitude);
                isFirstIn = false;
                //Toast.makeText(context,location.getAddrStr(),Toast.LENGTH_LONG).show();
            }
        }
    }
}

  (5)在MyOrientationListener中實現定位自己位置的功能,並實現地圖箭頭方向的指定:

package com.example.mymap;

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 Sensor mSensor;
    private Context mContext;
    private float lastX;
    private OnOrientationListener mOnOrientationListener;
    public MyOrientationListener(Context context) {
        this.mContext = context;
    }
    //方向改變
    @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;
        }

    }

    public void setOnOrientationListener(OnOrientationListener listener)
    {
        mOnOrientationListener=listener;
    }

    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);
    }

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

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

    }
}

  (6)在AddressToLatitudeLongitude實現具體地址向經緯度的解析:

package com.example.mymap;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class AddressToLatitudeLongitude {
    private String address;//地址
    private double Latitude;//緯度
    private double Longitude;//經度

    public AddressToLatitudeLongitude(String addr_str) {
        this.address = addr_str;
    }

    /**
     * 根據地址得到地理圖標
     */
    public void getLatAndLngByAddress() {
        String addr = "";
        String lat = "";
        String lng = "";
        try{
            addr = java.net.URLEncoder.encode(address,"UTF-8");//設置編碼
        } catch (UnsupportedEncodingException e){
            e.printStackTrace();
        }
        String url = String.format("http://api.map.baidu.com/geocoder/v2/?"
                +"&mcode=72:2B:6A:60:DC:44:F0:0F:2A:89:12:2A:53:5B:4E:C5:85:DC:B9:2B;com.example.mymap&"
                +"address=%s&ak=LXlkwMMGlu1fTWqxQRSaaixl6XcIjL3c&output=json",addr);
        URL myURL = null;
        URLConnection httpsConn = null;
        //進行轉碼
        try{
            myURL = new URL(url);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        try {
            httpsConn = (URLConnection)myURL.openConnection();//建立連接
            if (httpsConn != null){
                InputStreamReader insr = new InputStreamReader(
                        httpsConn.getInputStream(),"UTF-8");
                BufferedReader br = new BufferedReader(insr);
                String data = null;
                if((data = br.readLine())!=null){
                    System.out.println(data);
                    lat = data.substring(data.indexOf("\"lat\":")+("\"lat\":").length(), data.indexOf("},\"precise\""));
                    lng = data.substring(data.indexOf("\"lng\":")+("\"lng\":").length(), data.indexOf(",\"lat\""));
                }
                insr.close();
                br.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.Latitude = Double.parseDouble(lat);
        this.Longitude = Double.parseDouble(lng);
    }

    public double getLatitude() {
        return Latitude;
    }

    public double getLongitude() {

        return Longitude;
    }
 
}

  (7)對於一些在地圖中可能用的圖片等資源,暫時存儲到res文件夾下的drawable里面。

4.項目演示:

  完成以上內容之后就可以在真機上進行調試,運行項目代碼並演示:

 

  初始定位:                                                     實現功能:                                             

                        

   輸入地址搜索,定位到該位置並顯示經緯度:                                                        返回當前位置:

                               

至此整個項目結束。

5.參考文章:

  https://www.imooc.com/learn/238(慕課網—百度地圖在Android中的使用)

  https://blog.csdn.net/wl1710582732/article/details/73466031(實現定位)

  https://blog.csdn.net/xfhy_/article/details/52535436(地址與經緯度轉換)

  https://blog.csdn.net/need_just_word/article/details/78026057(解決線程中toast閃退的問題)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM