PS:最近一直在搞使用LBS实现定位.一般现在涉及到日常生活交易平台的app.貌似都需要使用定位.比如说美团外卖,我请客等app.
学习内容:
1.LBS定位的简单介绍.
2.在Map上添加地图覆盖物+地理编码+反地理编码
1.LBS定位的简单介绍
LBS:基站定位.我这里主要还是通过使用百度地图LBS实现定位.使用百度地图LBS实现定位需要做一些相关的准备工作.需要在LBS开放平台上注册自己的AK.有了这个AK.我们的应用才能够去调用百度地图的LBS去实现定位功能.
百度地图LBS:AK注册地址:http://lbsyun.baidu.com/apiconsole/key.
我们注册了LBS的账号之后就可以去创建应用的AK了.这里注册需要添加数字签名.数字签名的获取我直接说一种直接了当的方式.就是通过下图去查找.
黄圈部分就是我们需要添加的数字签名.这是最快也是最直接的方式.还有一种方式是通过cmd的方式进行获取.不过比较麻烦.我一般是使用这种方式去获取的.当输入了数字签名和包名之后.就会出现:
我们可以看到相关应用对应的AK.有了这个AK之后我们的应用才能够去调用.否则是无法实现定位的.那么这个AK的作用是使用在AndroidManifest文件当中的..
<application <!--name 可以自己命名 value 就是我们获取的AK--> <meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="0FFf1eth8qPtRnGakNXqAXkN"/> </application>
配置的方式如上.在application标签之间进行添加即可..同时我们还需要添加相关的权限.
<!-- 百度API所需权限 --> <uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.USE_CREDENTIALS" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
这是一些配置信息.我们还需要使用相关的jar包.导入了相应的jar包和.so文件之后.我们才能够真正的在自己的应用上进行相关的开发.需要使用的相关文件如下:
这里我们可以看到使用到了(.so)文件,这个文件其实是动态函数库.只不过是属于C语言层次的.jar包是Java层的一些相关接口.而.so文件则是linux c层的函数库.这里已经不仅仅是涉及到软件层次上的东西了.已经涉及到了Android内部组件的使用.内部硬件的使用自然要通过C语言去调用.因此(.so)文件是必须要使用的.相应的jar包和(.so)文件包大家可以去网上下载.
那么有了这些基础之后我们就可以真正的去开发我们的定位应用了.
2.在Map上添加地图覆盖物+地理编码+反地理编码
需要明确一个概念 Poi:
BaiDuMap API类中提供了多个类用于我们在地图上添加覆盖物: ArcOptions(弧线形覆盖物),PolygonOptions(多边形覆盖物),TextOptions(文字覆盖物),GroundOverlay(地形图图层覆盖物),PolylineOptions(折线形覆盖物),DotOptions(原点覆盖物),CircleOptions(圆形(空心)覆盖物),这些类都继承与OverlayOptions抽象类
我们在自定义完这些覆盖物之后,通过使用Overlay中的addOverlay()函数,将相应的覆盖物添加到其中就可以完成在地图上添加覆盖物了.
那么覆盖物有什么用?查了很多的资料都没有给我一个明确的概念.个人认为比如说显示一个区域范围内的一些相关的数据信息.那么这个范围就可以通过添加覆盖物去指定区域.从而去显示一个区域内有多少数据信息(比如:房产,某一区域的车辆数等等).
说了这么多.我们就看看如何去添加覆盖物.
i. 多边形覆盖物(PolygonOptions)
覆盖物的添加需要经过几个过程,首先我们需要定义一个坐标点,不难理解.就拿多边形覆盖物来说.我们需要定义多个坐标点.这些坐标点的连线才能够构成多边形.构成多边形之后进行一些属性配置.然后使用Overlay中的addOverlay()函数.就能够成功的在地图上添加覆盖物了.
LatLng pt1 = new LatLng(latitude+0.02,longitude); //参数:经度+纬度 LatLng pt2 = new LatLng(latitude-0.02,longitude); //构造完多个坐标点.. List<LatLng> points =new ArrayList<LatLng>(); //保存节点信息. PolygonOptions polygonoptions = new PolygonOptions(); //实例化多边形覆盖物对象. polygonpoints.points(points); //添加坐标点 polygonoptions.fillColor(0xAAFFFF00); //多边形填充颜色 polygonpoints.stroke(new Stroke(2,0xAAFFFF00)); //设置多边形边框信息 Overlay polygon = bdMap.addOverlay(polygonoptions); //添加覆盖物.
这样就可以完成在地图上添加覆盖物.我们也可以为这些覆盖物设置相关的监听事件.监听事件的设置如下..每一个覆盖物都属于Marker的点击事件.因此通过setOnMarkerClickListener就可以实现点击时的相关操作.
bdMap.setOnMarkerClickListener(new OnMarkerClickListener() { @Override public boolean onMarkerClick(Marker arg0) { // TODO Auto-generated method stub final LatLng latLng = arg0.getPosition(); if(arg0 == marker1){ Toast.makeText(getApplicationContext(), latLng.toString(), Toast.LENGTH_SHORT).show(); } return false; } });
PolygonOptions的其他函数
polygonoptions.visiable(boolean visiable); //设置可见性 polygonoptions.zIndex(int zIndex) //设置多边形 polygonoptions.extraInfo(Bundle extraInfo) // 设置多边形额外信息.
ii.TextOptions(文字覆盖物) 设置文字覆盖物需要注意文字的颜色,大小,位置和属性
LatLng latlng = new LatLng(latitude,longitude); 定义坐标点位置 TextOptions textoptions = new TextOptions(); //实例化对象. //rotate为旋转角度. positions为显示的位置. textoptions.bgColor(0xAAFFFF00).fontSize(28).fontColor(0xAAFFFF00).text("").rotate(-30).position(latlng); bdMap.addOverlay(textoptions); //在地图中进行添加 //其他函数: textoptions.align(int ,int ) 设置文字覆盖物对其方式 textoptions.extra(Bundle); textoptions.typeface(Typeface); 设置字体 textoptions.zIndex(int zIndex) textoptions.visiable(boolean visiable)
iii.GroundOverlay(地形图图层覆盖物)
地形图图层可以跟随地图进行平移,深入变换,位于地图和标注图之间,不会遮挡标注图信息.定义这个覆盖物的时候,需要指定宽高.API仅仅提供了两种方法去构建:
1.指定一个(LatLng),再用dimensions方法去指定宽度和高度.
2.使用positionFromBounds(LatLngBounds bounds) 表示一个地理范围.指定两个角坐标构造一个矩形范围.
这里使用到了BitmapDescripter,这个是BaiDuMap中的设置定位图标的方法.这个类主要和Overlay进行打交道,可以为Overlay设置图标信息.
LatLng southwest = new LatLng(latitude - 0.01, longitude - 0.012);//西南 LatLng northeast = new LatLng(latitude + 0.01, longitude + 0.012);//东北 LatLngBounds bounds =new LatLngBounds.Builder().include(southwest).include(northwest).build();//构建对象. BitmapDescriptor bitmap = BitmapDescriptorFactory.fromResource(R.drawable.icon); //图标添加. GroundOverlayOptions options = new GroundOverlayOptions(); options.image(bitmap); //显示的图片 options.positionFromBounds(bounds); //显示位置 options.transparency(0.7f); //显示的透明度 bdMap.addOverlay(options); //添加到地图中
iv.PolylineOptions(折线覆盖物)
折线覆盖物和多边形覆盖物的添加基本相同.需要添加多个坐标点..然后在点与点之间画线.
LatLng pt1 = new LatLng(latitude + 0.01, longitude); LatLng pt2 = new LatLng(latitude, longitude - 0.01); LatLng pt3 = new LatLng(latitude - 0.01, longitude - 0.01); LatLng pt5 = new LatLng(latitude, longitude + 0.01); List<LatLng> points = new ArrayList<LatLng>(); points.add(pt1); points.add(pt2); points.add(pt3); points.add(pt5); // PolylineOptions polylineOptions = new PolylineOptions(); polylineOptions.points(points); polylineOptions.color(0xFF000000); polylineOptions.width(4); bdMap.addOverlay(polylineOptions);
v.DotOptions(原点覆盖物)
原点覆盖物的添加需要定义圆心坐标,以及半径.
private void addDotOptions() { bdMap.clear(); DotOptions dotOptions = new DotOptions(); dotOptions.center(new LatLng(latitude, longitude));// 设置圆心坐标 dotOptions.color(0XFFfaa755);// 颜色 dotOptions.radius(25);// 设置半径 bdMap.addOverlay(dotOptions); }
vi.ArcOptions(弧形覆盖物)
弧形覆盖物的添加则需要制定弧的起点,中点,终点..制定了这三个点就可以画出相关的弧线.
private void addArcOptions() { bdMap.clear(); LatLng pt1 = new LatLng(latitude, longitude - 0.01); LatLng pt2 = new LatLng(latitude - 0.01, longitude - 0.01); LatLng pt3 = new LatLng(latitude, longitude + 0.01); ArcOptions arcOptions = new ArcOptions(); arcOptions.points(pt1, pt2, pt3);// 设置弧线的起点、中点、终点坐标 arcOptions.width(5);// 线宽 arcOptions.color(0xFF000000); bdMap.addOverlay(arcOptions);
}
vii.CircleOptions(圆形(空心)覆盖物)
圆形空心覆盖物其实和原点覆盖物差不多.只不过一个是实心圆,一个是空心圆.
private void addCircleOptions() { bdMap.clear(); CircleOptions circleOptions = new CircleOptions(); circleOptions.center(new LatLng(latitude, longitude));// 设置圆心坐标 circleOptions.fillColor(0XFFfaa755);// 圆的填充颜色 circleOptions.radius(150);// 设置半径 circleOptions.stroke(new Stroke(5, 0xAA00FF00));// 设置边框 bdMap.addOverlay(circleOptions);
viii.弹出窗覆盖物.
弹出窗窗口布局我们可以自己去定义.然后添加到Map当中就可以了.
private void displayInfoWindow(final LatLng latLng){ // 创建infowindow展示的view Button btn = new Button(getApplicationContext()); btn.setBackgroundResource(R.drawable.popup); btn.setText(""); BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromView(btn); // infowindow点击事件 OnInfoWindowClickListener infoWindowClickListener = new OnInfoWindowClickListener() { @Override public void onInfoWindowClick() { reverseGeoCode(latLng); //隐藏InfoWindow bdMap.hideInfoWindow(); } }; // 创建infowindow InfoWindow infoWindow = new InfoWindow(bitmapDescriptor, latLng, -47, infoWindowClickListener); // 显示InfoWindow bdMap.showInfoWindow(infoWindow); }
这些都是API为我们提供的相应接口..我们同样可以去自定义样式然后去适配..比如说一个覆盖物的Marker的样式比较复杂..想要使用这个复杂的布局去替换这个bitmap.那么我们就可以将布局转化成bitmap,然后在添加覆盖物的时候..直接添加我们转化的bitmap就可以了.
IX.实现自定义覆盖物
首先我们需要定义一个xml文件布局..这个布局可以非常的复杂.但是这个布局的最外层布局只允许是LinearLayout..
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:background="@drawable/popup" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:layout_width="35dp" android:layout_height="35dp" android:scaleType="centerCrop" android:padding="5dip" android:src="@drawable/head_1"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android :color/black" android:textSize="20sp" android:text="测试"/> </LinearLayout>
然后我们把当前这个布局转化成Bitmap.然后在直接定义一个Marker对象.在内部添加Bitmap就可以了.转化成Bitmap的函数其实也并不是特别的复杂.之所以布局文件需要使用LinearLayout进行布局,而不能够使用RelativeLayout.是因为使用了makeMeasureSpec函数.这个函数貌似只对Linearayout有效.这样就可以将我们的xml文件布局转化成bitmap.转化完之后就可以进行添加了..
private Bitmap getViewBitmap(View addViewContent) { addViewContent.setDrawingCacheEnabled(true); addViewContent.measure( View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); addViewContent.layout(0, 0, addViewContent.getMeasuredWidth(), addViewContent.getMeasuredHeight()); addViewContent.buildDrawingCache(); Bitmap cacheBitmap = addViewContent.getDrawingCache(); Bitmap bitmap = Bitmap.createBitmap(cacheBitmap); return bitmap; }
说了这么多仅仅是完成了覆盖物的添加,那么如何定位还是一回事..如何根据我们的地理坐标实现定位呢?或者是根据我们的位置获取到地理坐标呢?这就需要使用到地理编码和反地理编码..
地理编码:将我们当前的地理信息转化成相应的位置.
反地理编码:将我们当前的坐标转化成地理信息.(注:反地理编码需要在网络链接状态的良好的情况下,才能够实现)
那么如何去实现呢?其实非常的简单..这个函数就实现了地理信息的编码和反编码.
private void reverseGeoCode(LatLng latLng){ //创建地理编码检索实例 GeoCoder geoCoder = GeoCoder.newInstance(); //设置地理编码的监听. OnGetGeoCoderResultListener listener = new OnGetGeoCoderResultListener() { //反地理编码函数的返回结果 @Override public void onGetReverseGeoCodeResult(ReverseGeoCodeResult arg0) { // TODO Auto-generated method stub if(arg0 == null || arg0.error != SearchResult.ERRORNO.NO_ERROR){ Toast.makeText(getApplicationContext(), "没有查找到结果", Toast.LENGTH_SHORT).show(); } Toast.makeText(getApplicationContext(), "位置:"+arg0.getAddress(), Toast.LENGTH_SHORT).show(); } //地理编码的返回结果 @Override public void onGetGeoCodeResult(GeoCodeResult arg0) { // TODO Auto-generated method stub if(arg0 == null || arg0.error != SearchResult.ERRORNO.NO_ERROR){ Toast.makeText(getApplicationContext(), "没有查找到结果", Toast.LENGTH_SHORT).show(); } } }; //设置地理编码检索监听 geoCoder.setOnGetGeoCodeResultListener(listener); //反地理编码需要传递坐标点参数. geoCoder.reverseGeoCode(new ReverseGeoCodeOption().location(latLng)); }
地理编码和反地理编码都算是很好理解..通过使用API提供的接口.我们就可以轻松实现..通过创建地理编码检索对象,然后为对象设置相关的监听就可以了.地理编码和反地理编码仅仅能够确定我们的位置..但是如果想真正完成定位需要使用到定位的核心类.LocationClient.
X.LocationClient.
LocationClient是实现定位的核心类.定位服务的客户端.
locClient = new LocationClient(this); locClient.registerLocationListener(locListener); //定位模式 对象实例化 LocationClientOption option = new LocationClientOption(); option.setOpenGps(true); // 打开GPS option.setCoorType("bd09ll");// 设置坐标类型 option.setAddrType("all"); // 地理信息设置 option.setScanSpan(1000); // 设置扫描间隔 locClient.setLocOption(option); //添加定位模式 locClient.start(); //启动定位
只有实例化了LocationClient对象.我们才能够真正的实现定位.实例化对象后.我们需要设置定位的监听.
定位监听的设置:
BDLocationListener locListener = new BDLocationListener() { @Override public void onReceivePoi(BDLocation location) { } //定位请求回调函数 @Override public void onReceiveLocation(BDLocation location) { if (location == null || bdMap == null) { return; } // 构造定位数据 MyLocationData locData = new MyLocationData.Builder() .accuracy(location.getRadius())// .direction(100)// 方向 .latitude(location.getLatitude())// .longitude(location.getLongitude())// .build(); // 设置定位数据 bdMap.setMyLocationData(locData); latitude = location.getLatitude(); longitude = location.getLongitude(); // 第一次定位的时候,那地图中心点显示为定位到的位置 if (isFirstLoc) { isFirstLoc = false; LatLng ll = new LatLng(location.getLatitude(), location.getLongitude()); // MapStatusUpdate描述地图将要发生的变化 // MapStatusUpdateFactory生成地图将要反生的变化 MapStatusUpdate msu = MapStatusUpdateFactory.newLatLng(ll); bdMap.animateMapStatus(msu); // bdMap.setMyLocationEnabled(false); Toast.makeText(getApplicationContext(), location.getAddrStr(), Toast.LENGTH_SHORT).show(); } } };
添加了定位的监听以及定位时需要的相关配置参数.就可以真正的实现定位了..
最后附上一个源代码:
http://files.cnblogs.com/files/RGogoing/Map.rar