Android开发百度地图应用——实现定位功能

时间:2022-08-31 14:50:12

本随笔是为了记录课程中的一个安卓开发作业:做一个安卓端的地图应用,要求可以实现地图定位功能,输入一个地址后,可以在地图上定位到这个地址并给出经纬度(使用 Android Studio 开发)

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

1.在百度地图开放平台新建项目:

  应用名称自己确定,类型选择Android SDK,启用的服务都按照默认的勾选即可,最重要的是两个SHA1的确定,按照官方文档的说明只填写开发版的即可,但创建的时候发布版是必填项(官方文档应该更新了。。。。),因此这里两个版本全部填写为开发版本的SHA1,若后续发布则再回来修改发布版的即可(具体操作可以参考官方文档)。

 Android开发百度地图应用——实现定位功能

 

  按照官方说明获取SHA1,这里使用开发版,进入.android目录下输入指令:

Android开发百度地图应用——实现定位功能

 

  获取包名:

Android开发百度地图应用——实现定位功能

  按照以上步骤填写完之后会按照生成该项目的密钥,后续开发会使用:

Android开发百度地图应用——实现定位功能

2.对Android Studio进行相关配置:

  首先在百度地图开发平台下载会用到的百度开发包:

Android开发百度地图应用——实现定位功能

 

   下载解压之后,将里面的 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.项目演示:

  完成以上内容之后就可以在真机上进行调试,运行项目代码并演示:

  初始定位:                                                     实现功能:                                              输入地址搜索,定位到该位置并显示经纬度:                                                        返回当前位置:

  Android开发百度地图应用——实现定位功能                   Android开发百度地图应用——实现定位功能            Android开发百度地图应用——实现定位功能    Android开发百度地图应用——实现定位功能                   Android开发百度地图应用——实现定位功能

至此整个项目结束。

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闪退的问题)