編輯:關於Android編程
使用過百度地圖的同學知道,它有個街景功能,可以看到許多地方的實景。這裡就其街景內容的實現,進行下學習。
在百度地圖SDK的官網上可以看到,百度對開發者提供了很多相關的內容,方便我們進行學習。關於SDK的使用方法,包括jar包導入,*.so 動態庫的添加位置及AndroidManifest文件的配置不做為我們這裡討論的內容,官方文檔已經介紹的很詳細,不做無聊的搬運工。
這裡我們首先預覽下,今天最終要實現的效果圖
如圖所示,我們這裡的實現,就是兩個頁面的內容,一個是基礎的地圖MapView,一個是街景地圖PanoView。接下來,就這兩個頁面(Activity)分別展開來說。(由於GIF圖片大小限制,效果不是很理想,文章結尾有源碼地址,可以自己跑一下看一下效果先)
一般情況下,我們的應用程序都會有一個繼承自Application的類,用於實現一些初始化的方法,這裡可以在Application裡執行一些百度地圖初始化的工作,這也是官方提倡的方式。
public void onCreate() {
super.onCreate();
mInstance = this; SDKInitializer.initialize(getApplicationContext());
}
在Activity的OnCreate方法中實現
setContentView(R.layout.activity_mapview);
mMapView = (MapView) findViewById(R.id.bmapView);
上面這樣一段簡單的代碼,就可以在Activity中顯示出一個MapView,也就是我們最熟悉的地圖頁面,是不是很簡單,就像我們顯示一個TextView一樣。
這裡說明寫一下,按照官方的API指導文檔,使用MapView等百度地圖SDK所提供的各種實現,是需要去申請相關的key的,申請的方法在官網有著詳細的介紹,這裡就不再粘貼復制了;很多同學在使用MapView的時候發現,程序運行後地圖沒有顯示,顯示的都是一些方格子,這往往是由於key沒有申請,或申請的方式不當造成的
每次打開百度地圖,都會自動定位到我們當前所在的位置,或者是我們搜索某個特定的地方作為新的位置,整個地圖所呈現的區域都是新位置周邊的環境。這裡,關於地圖的定位和搜索的相關實現內容,就不展開來說,不當做此次的重點。
假設我們已通過定位(或者是搜索),定位了到了一個位置
**
* 假設我們當前的位置在此
*/
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 是一個BaiduMap的實例,通過MapView的getMap方法即可獲得。我們對地圖的各種操作,設置屬性都是基於這個實例進行。
通過上面的代碼,我們就可以將MapView的視圖更新到我們所想要的位置了。
按照百度地圖API的說法,我們添加到地圖上的小圖標統一稱為Marker。
//構建Marker圖標
bitmap = BitmapDescriptorFactory
.fromResource(R.drawable.icon_markc);
//構建MarkerOption,用於在地圖上添加Marker
option = new MarkerOptions()
.position(point)
.icon(bitmap);
//在地圖上添加Marker,並顯示
mBaiduMap.addOverlay(option);
通過上面的實現,我們就可以將一個小圖標添加到地圖層,作為標記。我們日常使用地圖時,所搜周邊後呈現的一系列小圓點就是如此(如下圖)
最後一步,實現顯示街景縮略圖的那個小彈框。
這裡首先自定義一下我們要添加到地圖層的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();
這樣,我們就可以根據當前位置,先檢測一下是否有街景可以顯示。這裡,如果當前位置有街景,我們就通過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);
}
}
}
這裡看一下,InfoWindow的說明及其構造函數
public class InfoWindow extends java.lang.Object
在地圖中顯示一個信息窗口,可以設置一個View作為該窗口的內容,也可以設置一個 BitmapDescriptor 作為該窗口的內容。
public InfoWindow(View view, LatLng position, int yOffset)
/**
通過傳入的 view 構造一個 InfoWindow, 此時只是利用該view
生成一個Bitmap繪制在地圖中,監聽事件由開發者實現。
Parameters:
view - InfoWindow 展示的 view
position - InfoWindow 顯示的地理位置
yOffset - InfoWindow Y 軸偏移量
*/
在Handler的handleMessage方法中,我們通過返回的url加載圖片,並將自定義的彈框View顯示到之前一步添加的marker偏上一點的地方(這就是InfoWindow的構造函數中-57的意義)
關於這個加載圖片的URL,可以參考這裡靜態圖API。
這樣,就實現了MapView頁面所有的內容。通過點擊InfoWindow,就可以跳轉到PanoView所在的界面去查看街景地圖。
接下來,我們將介紹PanoView街景地圖的實現。
街景地圖PanoView的顯示和基礎地圖MapView十分相似
首先是在布局文件中定義view
在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類中做一些初始化工作;對我們所使用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裡也需要做一些初始化的工作,最後就是通過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值切換全景場景
也就是說,不僅通過經緯度,而且可以通過別的方式實現街景地圖的功能。這裡我們就使用了大家最熟悉的經緯度,對於別的實現方式有興趣的同學,可以自己去探索一下。
如圖所示,將一個MapView顯示在PanoView之上;很自然的我們會寫出下面的布局方式:
這樣,我們在整個PanoView的左下角定義一個60x60大小的view用於顯示一個MapView。
街景SDK為我們提供了PanoramaViewListener這個接口,可以實現對從街景視圖開始繪制到完成繪制,對街景地圖的操作(如點擊,旋轉)的監聽。
這裡我們重點看一下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;
}
}
這裡不得不吐槽一下,官方所提供的API文檔,對這個onMessage回調方法中的參數居然沒有任何有價值的解釋。這裡的8213及12302完全是通過打印日志自己總結出的規律。
這樣,我們對於不同的操作,就可以通過Handler實現不同的UI效果。我們看一下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的偏轉角度,從而實現一種在左下方小地圖上呈現我們當前視角的效果。
好了,這樣就簡單模仿了一下百度地圖街景的部分實現功能,由於UI資源所限制,部分效果並非完全一致,這裡只是學習下而已。
代碼已上傳至github,點這裡即可查看。
分類 notification有以下幾種: 1>普通notification 1.內容標題 2.大圖標 3.內容 4.內容附
要研究的幾個問題 一、Behavior是什麼?為什麼要用Behavior? 二、怎麼使用Behavior? 三、從源碼角度看為什麼要這麼使用Behavior?一、Beha
概述當以Bitmap作為畫布材料時,可以繪制出以下各種圖案:demo/** * 圖形圖像處理:在Bitmap上繪畫 */public class MyBitMapView
WeTest導讀本文通過對內存洩漏(what)及其危害性(why)的介紹,引出在Unity環境下定位和修復內存洩漏的方法和工具(how)。最後提出了一些避免洩漏的方法與建