前一篇百度地图开发讲述"(二).定位城市位置和城市POI搜索",主要通过监听对象MKSearchListener类实现城市兴趣点POI(Point of Interest)搜索。该篇讲述定位当前自己的位置及使用getLastKnownLocation获取location总时为空值的问题

一. 定位当前位置的原理及实现

      定位当前位置可以通过LBS(Location Based Service,基于位置的服务),主要工作原理是利用无线网络Network或GPS定位方式确定移动设备所在的位置。
      其基本步骤如下:(参考郭神《Android第一行代码》)

      1.先实例LocationManager,getSystemService(Context.LOCATION_SERVICE)再确定获取系统的定位服务;
      2.选择位置提供器,通常会使用LocationManager.NETWORK_PROVIDER网络定位(精准度差、耗电少)或LocationManager.GPS_PROVIDER实现GPS定位(精准度高、耗电多);
      3.然后通过LocationManager的getLastKnownLocation()函数,它选择位置提供器provider得到Location对象;
      4.此时你已经获取了地理位置,如果手机移动可以通过LocationManager的另一个函数requestLocationUpdates()方法获取动态的位置信息;
      5.获取当前Location后需要加载到百度地图中,可以通过GeoPoint设置当前位置经度和纬度,并使用MyLocationOverlay载入该数据及添加当前位置覆盖物。

      其核心代码如下所示:

//定位private Button button1; private LocationManager locationManager;private String provider;/** * 定位自己位置 onCreate函数中点击按钮事件 */button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) { //获取所有位置提供器        locationManager =  (LocationManager) getSystemService(Context.LOCATION_SERVICE);        List providerList = locationManager.getProviders(true);        if(providerList.contains(LocationManager.NETWORK_PROVIDER)) { //网络提供器        provider = LocationManager.NETWORK_PROVIDER;        } else if(provider.contains(LocationManager.GPS_PROVIDER)) { //GPS提供器        provider = LocationManager.GPS_PROVIDER;        } else {        Toast.makeText(MainActivity.this, "No location provider to use",        Toast.LENGTH_SHORT).show();        return;        }        //获取到记录当前位置        Location location = locationManager.getLastKnownLocation(provider);        if(location!=null) {        //定位我的位置        MapController controller = mapView.getController();        controller.setZoom(16);        //latitude 纬度 longitude 经度        GeoPoint point =  new GeoPoint((int) (location.getLatitude()*1E6),        (int) (location.getLongitude()*1E6));        controller.setCenter(point); //设置地图中心        mapView.getOverlays().clear(); //清除地图上所有覆盖物        MyLocationOverlay locationOverlay = new MyLocationOverlay(mapView);        LocationData locationData = new LocationData();        locationData.latitude = location.getLatitude(); //纬度        locationData.longitude = location.getLongitude(); //经度        locationOverlay.setData(locationData);        //添加覆盖物        mapView.getOverlays().add(locationOverlay);        mapView.refresh(); //刷新        }}
     运行效果如下图所示:
    

二. 定位当前位置的问题


      但是此时你可能会遇到两个问题:
      第一个问题是有时候百度地图不能定位到当前位置,究其原因我发现代码获取的location总为空值,即:

      Location location = locationManager.getLastKnownLocation(provider);
      第二个问题就是在能定位当前位置的情况下,获取的位置总是存在偏移,向左下方偏移一定方位。
      其中第一个问题在getLastKnownLocation(provider)总是获取Null,据说是该函数获取的是上一次Location,而且它不是一次就能定位成功的,需要多次定位才能实现。通过在getLastKnownLocation()函数后添加循环多次定位如下代码:
location = locationManager.getLastKnownLocation(provider);while(location  == null)  {    mgr.requestLocationUpdates("gps", 60000, 1, locationListener);  }  
      其中locationListener是消息监听,具体代码如下所示,当位置发生变化时自定义函数显示新经纬坐标。参考:stackoverflow
private final LocationListener locationListener = new LocationListener() {      //位置发生改变后调用      public void onLocationChanged(Location location) {         //更新当前设备的新位置信息      showLocation(location);    }      //provider 被用户关闭后调用      public void onProviderDisabled(String provider) {      }      //provider 被用户开启后调用      public void onProviderEnabled(String provider) {             }      //provider 状态变化时调用      public void onStatusChanged(String provider, int status, Bundle extras) {      }  };
     但是很遗憾的是我采用这种方法并没有解决该问题,这就引出了“三.定位当前位置(源码)”内容。通过另外一种百度地图获取当前位置的方法实现,通过设置LocationClient获取,而且能解决这里提到的两个问题且相对精确的实现定位。

三. 定位当前位置(源码)


     此种方法参考xiaanming大神的博客,推荐大家阅读,讲述的非常好尤其是其实现细节,我主要是阐述该问题及提供一个可行方法罢了。
        http://blog.csdn.net/xiaanming/article/details/11380619
     主要通过locSDK的LocationClient实现显示当前位置,同时此种方法如果遇到没有显示地图。其原因是:首先需要在AndroidManifest.xml中添加如下代码。
      参考:http://bbs.csdn.net/topics/390382448

               ....               
      1.运行效果如下图所示。
      它能获取当前位置,并且通过监听函数5秒间隔获取一次;
      public class BDLocationListenerImpl implements BDLocationListener
      在监听函数中富国flag!=1表示没有点击“定位”按钮则不实现监听定位当前位置功能;同时结合前面第二篇文章POI搜索及城市定位功能。

      下载地址Demo:http://download.csdn.net/detail/eastmount/8349191

    
    
     
 2.注意需要引入SDK包括LocSDK_3.1.jar和liblocSDK3.so,其工程结构如下所示:

     
      3.MainActivity.java文件
public class MainActivity extends Activity {//BMapManager 对象管理地图、定位、搜索功能private BMapManager mBMapManager;  private MapView mapView = null;               //地图主控件 private MapController mMapController = null;  //地图控制 MKMapViewListener mMapListener = null;        //处理地图事件回调 private MKSearch mMKSearch;                   //定义搜索服务类//搜索private EditText keyWordEditText;  private EditText cityEditText;private Button queryButton;private static StringBuilder sb;  private MyLocationOverlay myLocationOverlay;//定位private Button button1; private LocationManager locationManager;private String provider;//方法二 定位位置    private BDLocation myLocation;      private LocationData mLocData;        //用户位置信息     private LocationClient mLocClient;    //定位SDK的核心类     private MyLocationOverlay locationOverlay = null;  //我的图层    private PopupOverlay pop;             //弹出pop 我的位置    private int flag=0;                   //标记变量 定位我的位置=1 POI为2    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);                /**         * 创建对象BMapManager并初始化操作         * V2.3.1中init(APIKey,null) V2.4.1在AndroidManifest中赋值AK         * 注意 初始化操作在setContentView()前         */        mBMapManager = new BMapManager(getApplication());          mBMapManager.init(null);         setContentView(R.layout.activity_main);          //获取对象        mapView = (MapView) findViewById(R.id.map_view);          cityEditText = (EditText) findViewById(R.id.city_edittext);        keyWordEditText = (EditText) findViewById(R.id.keyword_edittext);        queryButton = (Button) findViewById(R.id.query_button);        button1 = (Button) findViewById(R.id.button1);        //地图初始化        mMapController = mapView.getController();   //获取地图控制器        mMapController.enableClick(true);           //设置地图是否响应点击事件        mMapController.setZoom(16);                 //设置地图缩放级别        mapView.setBuiltInZoomControls(true);       //显示内置缩放控件       /** * 定位自己位置 */         //方法二        button1.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {flag = 1;        locationManager =  (LocationManager) getSystemService(Context.LOCATION_SERVICE);        //设置缩放级别 级别越高地图显示精细            MapController controller = mapView.getController();controller.setZoom(16);//实例化定位服务 LocationClient类必须在主线程中声明 并注册定位监听接口        mLocClient = new LocationClient(getApplicationContext());          mLocClient.registerLocationListener(new BDLocationListenerImpl());         /**          * LocationClientOption 该类用来设置定位SDK的定位方式。          */          LocationClientOption option = new LocationClientOption();          option.setOpenGps(true); //打开GPRS          option.setAddrType("all"); //返回的定位结果包含地址信息          option.setCoorType("bd09ll"); //返回的定位结果是百度经纬度,默认值gcj02          option.setPriority(LocationClientOption.GpsFirst); // 设置GPS优先          option.setScanSpan(5000);   //设置发起定位请求的间隔时间为5000ms          option.disableCache(false); //禁止启用缓存定位        mLocClient.setLocOption(option);  //设置定位参数        mLocClient.start();  // 调用此方法开始定位        //定位图层初始化                mapView.getOverlays().clear();            locationOverlay= new MyLocationOverlay(mapView);          //实例化定位数据,并设置在我的位置图层          mLocData = new LocationData();          locationOverlay.setData(mLocData);         //添加定位图层          mapView.getOverlays().add(locationOverlay);          //修改定位数据后刷新图层生效          mapView.refresh(); }        });                /**         * 初始化MKSearch 调用城市和POI搜索           */        mMKSearch = new MKSearch();        mMKSearch.init(mBMapManager, new MySearchListener());         queryButton.setOnClickListener(new OnClickListener() {              @Override              public void onClick(View v) {              if(flag==1) {pop.hidePop();flag = 2;}                mMapController = mapView.getController();            mMapController.setZoom(10);                  sb = new StringBuilder();  //内容清空                  //输入正确城市关键字                String city = cityEditText.getText().toString().trim();                  String keyWord = keyWordEditText.getText().toString().trim();                  if(city.isEmpty()) { //默认城市设置为贵阳                city="贵阳";                }                //如果关键字为空只搜索城市 GEO搜索                 if(keyWord.isEmpty()) {                mMKSearch.geocode(city, city); //具体地址和城市 geocode(adress, city)                }                 else {                //搜索城市+关键字                     mMKSearch.setPoiPageCapacity(10);  //每页返回POI数                    mMKSearch.poiSearchInCity(city, keyWord);                 }            }          });      }        /**      * 定位接口,需要实现两个方法       * 参考 http://blog.csdn.net/xiaanming/article/details/11380619     */      public class BDLocationListenerImpl implements BDLocationListener {            /**          * 接收异步返回的定位结果,参数是BDLocation类型参数          */          @Override          public void onReceiveLocation(BDLocation location) {              if (location == null || flag != 1) {                  return;              }              MapController controller = mapView.getController();//设置经纬度            MainActivity.this.myLocation = location;                mLocData.latitude = location.getLatitude();              mLocData.longitude = location.getLongitude();              GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6),(int) (location.getLongitude() * 1E6));            controller.setCenter(point);            //如果不显示定位精度圈,将accuracy赋值为0即可              //mLocData.accuracy = location.getRadius();              mLocData.direction = location.getDerect();              mLocData.accuracy = 0;            //将定位数据设置到定位图层里              locationOverlay.setData(mLocData);              //更新图层数据执行刷新后生效              mapView.refresh();              //覆盖物            if(flag==1) {            //添加图形pop = new PopupOverlay(mapView, new PopupClickListener() { @Overridepublic void onClickedPopup(int index) {}});Bitmap[] bitmaps = new Bitmap[3];try {bitmaps[0] = BitmapFactory.decodeResource(getResources(),R.drawable.left);bitmaps[1] = BitmapFactory.decodeResource(getResources(),R.drawable.middle);bitmaps[2] = BitmapFactory.decodeResource(getResources(),R.drawable.right);} catch (Exception e) {e.printStackTrace();}pop.showPopup(bitmaps, point, 18);            }              }                  /**          * 接收异步返回的POI查询结果,参数是BDLocation类型参数          */          @Override          public void onReceivePoi(BDLocation poiLocation) {                        }      }          @Overrideprotected void onResume() {mapView.onResume();if (mBMapManager != null) {mBMapManager.start();}super.onResume();}        @Overrideprotected void onDestroy() {mapView.destroy();if (mBMapManager != null) {mBMapManager.destroy();mBMapManager = null;}super.onDestroy();}@Overrideprotected void onPause() {mapView.onPause();if (mBMapManager != null) {mBMapManager.stop();}super.onPause();}/**      * 内部类实现MKSearchListener接口,用于实现异步搜索服务      */      public class MySearchListener implements MKSearchListener {              /**          * 根据经纬度搜索地址信息结果          * 同时mMKSearch.geocode(city, city)搜索城市返回至该函数         *          * @param result 搜索结果          * @param iError 错误号(0表示正确返回)          */          @Override          public void onGetAddrResult(MKAddrInfo result, int iError) {          if (result == null) {                  return;              }              StringBuffer sbcity = new StringBuffer();              sbcity.append(result.strAddr).append("\n");   //经纬度所对应的位置          mapView.getOverlays().clear();            //清除地图上已有的所有覆盖物              mMapController.setCenter(result.geoPt);       //置为地图中心            //添加原点并刷新            LocationData locationData = new LocationData();            locationData.latitude = result.geoPt.getLatitudeE6();            locationData.longitude = result.geoPt.getLongitudeE6();            myLocationOverlay = new MyLocationOverlay(mapView);            myLocationOverlay.setData(locationData);mapView.getOverlays().add(myLocationOverlay);mapView.refresh();            // 通过AlertDialog显示地址信息            new AlertDialog.Builder(MainActivity.this)              .setTitle("显示当前城市地图")              .setMessage(sbcity.toString())              .setPositiveButton("关闭", new DialogInterface.OnClickListener() {                  public void onClick(DialogInterface dialog, int whichButton) {                      dialog.dismiss();                  }              }).create().show();        }            /**          * POI搜索结果(范围检索、城市POI检索、周边检索)          *           * @param result 搜索结果          * @param type 返回结果类型(11,12,21:poi列表 7:城市列表)          * @param iError 错误号(0表示正确返回)          */          @Override          public void onGetPoiResult(MKPoiResult result, int type, int iError) {          if (result == null) {                  return;              }            //获取POI并显示        mapView.getOverlays().clear();             PoiOverlay poioverlay = new PoiOverlay(MainActivity.this, mapView);  //显示POI            poioverlay.setData(result.getAllPoi()); //设置搜索到的POI数据              mapView.getOverlays().add(poioverlay);  //兴趣点标注在地图上            mapView.refresh();             //设置其中一个搜索结果所在地理坐标为地图的中心              if(result.getNumPois() > 0) {                  MKPoiInfo poiInfo = result.getPoi(0);                  mMapController.setCenter(poiInfo.pt);              }              //添加StringBuffer 遍历当前页返回的POI (默认只返回10个)            sb.append("共搜索到").append(result.getNumPois()).append("个POI\n");              for (MKPoiInfo poiInfo : result.getAllPoi()) {                  sb.append("名称:").append(poiInfo.name).append("\n");            }         // 通过AlertDialog显示当前页搜索到的POI              new AlertDialog.Builder(MainActivity.this)              .setTitle("搜索到的POI信息")              .setMessage(sb.toString())              .setPositiveButton("关闭", new DialogInterface.OnClickListener() {                  public void onClick(DialogInterface dialog, int whichButton) {                      dialog.dismiss();                  }              }).create().show();        }            /**          * 驾车路线搜索结果          *           * @param result 搜索结果          * @param iError 错误号(0表示正确返回)          */          @Override          public void onGetDrivingRouteResult(MKDrivingRouteResult result, int iError) {          }                  /**          * 公交换乘路线搜索结果          *           * @param result 搜索结果          * @param iError 错误号(0表示正确返回)          */          @Override          public void onGetTransitRouteResult(MKTransitRouteResult result, int iError) {          }            /**          * 步行路线搜索结果          *           * @param result 搜索结果          * @param iError 错误号(0表示正确返回)          */          @Override          public void onGetWalkingRouteResult(MKWalkingRouteResult result, int iError) {          }@Overridepublic void onGetBusDetailResult(MKBusLineResult arg0, int arg1) {// TODO Auto-generated method stub}@Overridepublic void onGetPoiDetailSearchResult(int arg0, int arg1) {// TODO Auto-generated method stub}@Overridepublic void onGetShareUrlResult(MKShareUrlResult arg0, int arg1, int arg2) {// TODO Auto-generated method stub}@Overridepublic void onGetSuggestionResult(MKSuggestionResult arg0, int arg1) {// TODO Auto-generated method stub}    }      }
     4.布局文件activity_main.xml,同时添加图片left.png、middle.png和right.png
                                            
      5.设置AndroidMainfest.xml权限及服务,同时设置百度地图APIKey,第一篇文章有详细讲述。
<?xml version="1.0" encoding="utf-8"?>                                                                                                                                                                                                             
      最后希望文章对大家有所帮助,刚刚接触android开发百度地图,而且还是使用V2.4.1版本,如果有错误或不足之处,还请海涵!建议大家看看官方文档和百度提供的Demo.文章主要参考百度官方文档、xiaanming大神博客和郭神《Android第一行代码》及我前面的两篇文章.
      下载地址:http://download.csdn.net/detail/eastmount/8349191
        [android] 百度地图开发 (一).申请AK显示地图及解决显示空白网格问题 
       [android] 百度地图开发 (二).定位城市位置和城市POI搜索 

     (By:Eastmount 2015-01-11 夜2点 http://blog.csdn.net/eastmount/

更多相关文章

  1. android 之 百度地图
  2. android中引用项目工程中的sqlite文件
  3. Android的PopupWindow的使用,根据点击位置显示弹窗
  4. Android地图Key
  5. 使用Android(安卓)RatingBar时踩过的坑
  6. 【Android】高仿大众点评中的范围选择控件之RangeSeekBar
  7. Android(安卓)特色开发,基于位置的服务
  8. Android实现贪吃蛇游戏一:游戏界面及控制
  9. android 更改avd路径

随机推荐

  1. Android的开机启动流程概述
  2. Android中输出HTML格式下的文字
  3. android之自定义控件一控件的呈现机制
  4. 用百度API高仿微信定位demo
  5. [置顶] Android(安卓)listview checkbox
  6. (android/swig实现)用c/c++混合编程方式
  7. Android 模拟HTTP 协议进行表单提交
  8. Eclipse下Android的代码调试
  9. Android 系统(243)---Android进程系列第一
  10. win10通过网线连接树莓派和PC方法(Android