本随笔是为了记录课程中的一个安卓开发作业:做一个安卓端的地图应用,要求可以实现地图定位功能,输入一个地址后,可以在地图上定位到这个地址并给出经纬度(使用 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闪退的问题)