使用過百度地圖的同學知道,它有個街景功能,可以看到許多地方的實景。這里就其街景內容的實現,進行下學習。
在百度地圖SDK的官網上可以看到,百度對開發者提供了很多相干的內容,方便我們進行學習。關于SDK的使用方法,包括jar包導入,*.so 動態庫的添加位置及AndroidManifest文件的配置不做為我們這里討論的內容,官方文檔已介紹的很詳細,不做無聊的搬運工。
這里我們首先預覽下,今天終究要實現的效果圖
如圖所示,我們這里的實現,就是兩個頁面的內容,1個是基礎的地圖MapView,1個是街景地圖PanoView。接下來,就這兩個頁面(Activity)分別展開來講。(由于GIF圖片大小限制,效果不是很理想,文章結尾有源碼地址,可以自己跑1下看1下效果先)
<?xml version="1.0" encoding="utf⑻"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.baidu.mapapi.map.MapView
android:id="@+id/bmapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" />
</LinearLayout>
1般情況下,我們的利用程序都會有1個繼承自Application的類,用于實現1些初始化的方法,這里可以在Application里履行1些百度地圖初始化的工作,這也是官方提倡的方式。
public void onCreate() {
super.onCreate();
mInstance = this; SDKInitializer.initialize(getApplicationContext());
}
在Activity的OnCreate方法中實現
setContentView(R.layout.activity_mapview);
mMapView = (MapView) findViewById(R.id.bmapView);
上面這樣1段簡單的代碼,就能夠在Activity中顯示出1個MapView,也就是我們最熟習的地圖頁面,是否是很簡單,就像我們顯示1個TextView1樣。
這里說明寫1下,依照官方的API指點文檔,使用MapView等百度地圖SDK所提供的各種實現,是需要去申請相干的key的,申請的方法在官網有著詳細的介紹,這里就不再粘貼復制了;很多同學在使用MapView的時候發現,程序運行后地圖沒有顯示,顯示的都是1些方格子,這常常是由于key沒有申請,或申請的方式不當釀成的
每次打開百度地圖,都會自動定位到我們當前所在的位置,或是我們搜索某個特定的地方作為新的位置,全部地圖所顯現的區域都是新位置周邊的環境。這里,關于地圖的定位和搜索的相干實現內容,就不展開來講,不當作此次的重點。
假定我們已通過定位(或是搜索),定位了到了1個位置
**
* 假定我們當前的位置在此
*/
private final double latitude = 39.963175;
private final double longitude = 116.400244;
這個位置依照新聞里常聽到的說法就是,東經116.40度,北緯39.96度,位于北京市東城區舊鼓樓大街丙1號。
接下來,我們要做的就是將MapView的視圖更新到我們“定位”的位置,這個位置周邊的地圖才是我們關心的。
mBaiduMap = mMapView.getMap();
//定義Maker坐標點
point = new LatLng(latitude, longitude);
//定義地圖狀態
final MapStatus mMapStatus = new MapStatus.Builder()
.target(point)
.zoom(18)
.build();
//定義MapStatusUpdate對象,以便描寫地圖狀態將要產生的變化
MapStatusUpdate mMapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mMapStatus);
//改變地圖狀態
mBaiduMap.setMapStatus(mMapStatusUpdate);
這里的mBaiduMap 是1個BaiduMap的實例,通過MapView的getMap方法便可取得。我們對地圖的各種操作,設置屬性都是基于這個實例進行。
通過上面的代碼,我們就能夠將MapView的視圖更新到我們所想要的位置了。
依照百度地圖API的說法,我們添加到地圖上的小圖標統1稱為Marker。
//構建Marker圖標
bitmap = BitmapDescriptorFactory
.fromResource(R.drawable.icon_markc);
//構建MarkerOption,用于在地圖上添加Marker
option = new MarkerOptions()
.position(point)
.icon(bitmap);
//在地圖上添加Marker,并顯示
mBaiduMap.addOverlay(option);
通過上面的實現,我們就能夠將1個小圖標添加到地圖層,作為標記。我們平常使用地圖時,所搜周邊后顯現的1系列小圓點就是如此(以下圖)
最后1步,實現顯示街景縮略圖的那個小彈框。
這里首先自定義1下我們要添加到地圖層的View。
view = LayoutInflater.from(mContext).inflate(R.layout.pano_overlay, null);
pic = (ImageView) view.findViewById(R.id.panoImageView);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext, PanoDemoMain.class);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
startActivity(intent);
}
});
這里pic這個ImageView用于顯示我們要展現的街景縮略圖。pano_overlay是全部彈框的布局,很簡單,這里就不貼代碼了。
同時,我們為這個自定義View設置點擊事件,方便我們跳轉到PanoView街景地圖頁面,并且將當前位置傳遞過去。
由于祖國地大物博,所以街景的覆蓋并不是百分之百,所以說,不是每一個地方都有街景可以顯示,有些鳥不拉屎的地方是看不到的。那我們怎樣知道甚么地方有街景呢?API為我們提供了很好的檢測方法
new Thread(new Runnable() {
@Override
public void run() {
PanoramaRequest request = PanoramaRequest.getInstance(mContext);
BaiduPanoData locationPanoData = request.getPanoramaInfoByLatLon(longitude, latitude);
//開發者可以判斷是不是有外景(街景)
if (locationPanoData.hasStreetPano()) {
String url = baseUrl + locationPanoData.getPid();
Message message = new Message();
message.what = 0x01;
message.obj = url;
handler.sendMessage(message);
}
}
}).start();
這樣,我們就能夠根據當前位置,先檢測1下是不是有街景可以顯示。這里,如果當前位置有街景,我們就通過Handler通知主線程去更新UI
private class myHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x01) {
String url = (String) msg.obj;
Glide.with(mContext).load(url).into(pic);
InfoWindow mInfoWindow = new InfoWindow(view, point, -57);
//顯示InfoWindow
mBaiduMap.showInfoWindow(mInfoWindow);
}
}
}
這里看1下,InfoWindow的說明及其構造函數
public class InfoWindow extends java.lang.Object
在地圖中顯示1個信息窗口,可以設置1個View作為該窗口的內容,也能夠設置1個 BitmapDescriptor 作為該窗口的內容。
public InfoWindow(View view, LatLng position, int yOffset)
/**
通過傳入的 view 構造1個 InfoWindow, 此時只是利用該view
生成1個Bitmap繪制在地圖中,監聽事件由開發者實現。
Parameters:
view - InfoWindow 展現的 view
position - InfoWindow 顯示的地理位置
yOffset - InfoWindow Y 軸偏移量
*/
在Handler的handleMessage方法中,我們通過返回的url加載圖片,并將自定義的彈框View顯示到之前1步添加的marker偏上1點的地方(這就是InfoWindow的構造函數中⑸7的意義)
關于這個加載圖片的URL,可以參考這里靜態圖API。
這樣,就實現了MapView頁面所有的內容。通過點擊InfoWindow,就能夠跳轉到PanoView所在的界面去查看街景地圖。
接下來,我們將介紹PanoView街景地圖的實現。
街景地圖PanoView的顯示和基礎地圖MapView10分類似
首先是在布局文件中定義view
<com.baidu.lbsapi.panoramaview.PanoramaView
android:id="@+id/panorama"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:visibility="visible" />
在Activity的OnCreate方法中
PanoDemoApplication app = (PanoDemoApplication) this.getApplication();
if (app.mBMapManager == null) {
app.mBMapManager = new BMapManager(app);
app.mBMapManager.init(new PanoDemoApplication.MyGeneralListener());
}
mPanoView = (PanoramaView) findViewById(R.id.panorama);
mPanoView.setPanorama(longitude, latitude);
這里一樣需要的是在Application類中做1些初始化工作;對我們所使用key的有效性進行檢測。
public void initEngineManager(Context context) {
if (mBMapManager == null) {
mBMapManager = new BMapManager(context);
}
if (!mBMapManager.init(new MyGeneralListener())) {
Toast.makeText(PanoDemoApplication.getInstance().getApplicationContext(), "BMapManager 初始化毛病!",
Toast.LENGTH_LONG).show();
}
}
// 經常使用事件監聽,用來處理通常的網絡毛病,授權驗證毛病等
static class MyGeneralListener implements MKGeneralListener {
@Override
public void onGetPermissionState(int iError) {
// 非零值表示key驗證未通過
if (iError != 0) {
// 授權Key毛病:
Toast.makeText(PanoDemoApplication.getInstance().getApplicationContext(),
"請在AndoridManifest.xml中輸入正確的授權Key,并檢查您的網絡連接是不是正常!error: " + iError, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(PanoDemoApplication.getInstance().getApplicationContext(), "key認證成功", Toast.LENGTH_LONG)
.show();
}
}
}
同時在Activity里也需要做1些初始化的工作,最后就是通過PanoView的setPanorama()方法實現街景的顯示。
關于這里用到的setPanorama(),根據API我們可以看到
public void setPanorama(java.lang.String pid)
//根據全景pid值切換全景場景
public void setPanorama(int x,int y)
//根據百度墨卡托投影坐標切換全景場景
public void setPanorama(double longitude,double latitude)
//根據百度經緯度坐標切換全景場景
public void setPanoramaByUid(java.lang.String uid,
int panoType)
//根據uid值切換全景場景
也就是說,不但通過經緯度,而且可以通過別的方式實現街景地圖的功能。這里我們就使用了大家最熟習的經緯度,對別的實現方式有興趣的同學,可以自己去探索1下。
如圖所示,將1個MapView顯示在PanoView之上;很自然的我們會寫出下面的布局方式:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.baidu.lbsapi.panoramaview.PanoramaView
android:id="@+id/panorama"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:visibility="visible" />
<LinearLayout
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:background="#00ffffff">
<com.baidu.mapapi.map.MapView
android:id="@+id/bmapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:padding="20dp" />
</LinearLayout>
</RelativeLayout>
這樣,我們在全部PanoView的左下角定義1個60x60大小的view用于顯示1個MapView。
街景SDK為我們提供了PanoramaViewListener這個接口,可以實現對從街景視圖開始繪制到完成繪制,對街景地圖的操作(如點擊,旋轉)的監聽。
這里我們重點看1下onMessage(String msgName, int msgType)這個回調方法。
public void onMessage(String msgName, int msgType) {
Log.e(LTAG, "msgName--->" + msgName + ", msgType--->" + msgType);
switch (msgType) {
case 8213:
//旋轉
Log.e(PanoViewActivity.class.getSimpleName(), "now,the heading is " + mPanoView.getPanoramaHeading());
Message message = new Message();
message.what = ACTION_DRAG;
message.arg1 = (int) mPanoView.getPanoramaHeading();
handler.sendMessage(message);
break;
case 12302:
//點擊
Log.e(PanoViewActivity.class.getSimpleName(), "clicked");
Message msg = new Message();
msg.what = ACTION_CLICK;
handler.sendMessage(msg);
break;
default:
break;
}
}
這里不能不吐槽1下,官方所提供的API文檔,對這個onMessage回調方法中的參數竟然沒有任何有價值的解釋。這里的8213及12302完全是通過打印日志自己總結出的規律。
這樣,我們對不同的操作,就能夠通過Handler實現不同的UI效果。我們看1下handler的實現:
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case ACTION_CLICK:
if (titleVisible) {
titleVisible = false;
textTitle.startAnimation(animationHide);
textTitle.setVisibility(View.GONE);
sv.setVisibility(View.GONE);
} else {
titleVisible = true;
textTitle.startAnimation(animationShow);
textTitle.setVisibility(View.VISIBLE);
sv.setVisibility(View.VISIBLE);
}
break;
case ACTION_DRAG:
float heading = (float) msg.arg1;
mBaiduMap.clear();
//構建MarkerOption,用于在地圖上添加Marker
option = new MarkerOptions()
.position(point)
.rotate(360-heading)
.icon(bitmap);
//在地圖上添加Marker,并顯示
mBaiduMap.addOverlay(option);
break;
default:
break;
}
}
}
這里的處理就分兩種情況:
我們仿照百度地圖的樣式,實現標題欄及MapView的隱藏,并添加動畫,這樣可以方便用戶全屏更清晰的視察街景內容。
上面我們說過對MapView添加Marker的方法,這里就派上用處了。隨著我們對PanoView的不斷拖拽旋轉,通過其getPanoramaHeading() 可以得到當前視角的偏航角。
在UI線程中,我們可以通過不斷移除和添加Marker,并設置不同的marker的偏轉角度,從而實現1種在左下方小地圖上顯現我們當前視角的效果。
好了,這樣就簡單模仿了1下百度地圖街景的部份實現功能,由于UI資源所限制,部份效果并不是完全1致,這里只是學習下而已。
代碼已上傳至github,點這里便可查看。
上一篇 丑數
下一篇 HA 高可用軟件系統保養指南