Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 仿百度地圖街景實現

仿百度地圖街景實現

編輯:關於Android編程

使用過百度地圖的同學知道,它有個街景功能,可以看到許多地方的實景。這裡就其街景內容的實現,進行下學習。

在百度地圖SDK的官網上可以看到,百度對開發者提供了很多相關的內容,方便我們進行學習。關於SDK的使用方法,包括jar包導入,*.so 動態庫的添加位置及AndroidManifest文件的配置不做為我們這裡討論的內容,官方文檔已經介紹的很詳細,不做無聊的搬運工。

效果圖

這裡我們首先預覽下,今天最終要實現的效果圖

靜態圖1


靜態圖2


效果圖

如圖所示,我們這裡的實現,就是兩個頁面的內容,一個是基礎的地圖MapView,一個是街景地圖PanoView。接下來,就這兩個頁面(Activity)分別展開來說。(由於GIF圖片大小限制,效果不是很理想,文章結尾有源碼地址,可以自己跑一下看一下效果先)

地圖MapView實現

地圖MapView的簡單顯示

布局文件



    

    

    

Application

一般情況下,我們的應用程序都會有一個繼承自Application的類,用於實現一些初始化的方法,這裡可以在Application裡執行一些百度地圖初始化的工作,這也是官方提倡的方式。

public void onCreate() {
        super.onCreate();
        mInstance = this;     SDKInitializer.initialize(getApplicationContext());
        }

Activity

在Activity的OnCreate方法中實現

setContentView(R.layout.activity_mapview);
mMapView = (MapView) findViewById(R.id.bmapView);

上面這樣一段簡單的代碼,就可以在Activity中顯示出一個MapView,也就是我們最熟悉的地圖頁面,是不是很簡單,就像我們顯示一個TextView一樣。

這裡說明寫一下,按照官方的API指導文檔,使用MapView等百度地圖SDK所提供的各種實現,是需要去申請相關的key的,申請的方法在官網有著詳細的介紹,這裡就不再粘貼復制了;很多同學在使用MapView的時候發現,程序運行後地圖沒有顯示,顯示的都是一些方格子,這往往是由於key沒有申請,或申請的方式不當造成的

MapView顯示到當前位置

每次打開百度地圖,都會自動定位到我們當前所在的位置,或者是我們搜索某個特定的地方作為新的位置,整個地圖所呈現的區域都是新位置周邊的環境。這裡,關於地圖的定位和搜索的相關實現內容,就不展開來說,不當做此次的重點。

假設我們已通過定位(或者是搜索),定位了到了一個位置

**
 * 假設我們當前的位置在此
 */
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的視圖更新到我們所想要的位置了。

添加View到MapView

添加Marker

按照百度地圖API的說法,我們添加到地圖上的小圖標統一稱為Marker。

//構建Marker圖標
        bitmap = BitmapDescriptorFactory
                .fromResource(R.drawable.icon_markc);

        //構建MarkerOption,用於在地圖上添加Marker
        option = new MarkerOptions()
                .position(point)
                .icon(bitmap);
        //在地圖上添加Marker,並顯示
        mBaiduMap.addOverlay(option);

通過上面的實現,我們就可以將一個小圖標添加到地圖層,作為標記。我們日常使用地圖時,所搜周邊後呈現的一系列小圓點就是如此(如下圖)

marker示意圖

ShowInfoWindow使用

最後一步,實現顯示街景縮略圖的那個小彈框。

這裡首先自定義一下我們要添加到地圖層的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街景地圖的實現。

街景地圖PanoViewActivity實現

街景地圖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上面

如圖所示,將一個MapView顯示在PanoView之上;很自然的我們會寫出下面的布局方式:



        

        

            
        
    

這樣,我們在整個PanoView的左下角定義一個60x60大小的view用於顯示一個MapView。

實現對街景視圖操作的監聽

街景SDK為我們提供了PanoramaViewListener這個接口,可以實現對從街景視圖開始繪制到完成繪制,對街景地圖的操作(如點擊,旋轉)的監聽。

API

這裡我們重點看一下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,點這裡即可查看。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved