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