本隨筆是為了記錄課程中的一個安卓開發作業:做一個安卓端的地圖應用,要求可以實現地圖定位功能,輸入一個地址后,可以在地圖上定位到這個地址並給出經緯度(使用 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閃退的問題)