最近在搞一個地圖應用,主要也沒什麼技術痛點,主要就是定位,還有就是對應的資訊顯示,這裡就介紹一下百度的地圖應用吧,有興趣的同學可以看一下百度API Android開發裡面的例子,裡面的代碼主要就是寫得有點亂,這裡自己做一下總結。
首先我們來看一下:如下
要實現如這樣的效果,接下來我們一步一步來實現:
首先要註冊百度服務,這個我就不說了,大家可以百度一下,網上有很多的。
http://developer.baidu.com/map/sdk-android.htm
接下來下載百度Android SDK
http://developer.baidu.com/map/sdkandev-download.htm
我們下載Android SDKv2.1.1這個包
直接解壓BaiduMap_AndroidSDK_v2.1.1_Sample.zip這個壓縮包,把包裡的libs目錄下的所有檔案都拷到我們自己的工程的libs裡
這裡麵包含了定位跟地圖調用的方法。接下來就是給我們的工程加入一些許可權。(許可權不知道在哪設?Oh,my God)
那好吧,我來說一下吧,把Sample工程裡的AndroidManifest.xml中的
<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" /> <permission android:name="android.permission.BAIDU_LOCATION_SERVICE" > </permission> <uses-permission android:name="android.permission.BAIDU_LOCATION_SERVICE" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" > </uses-permission> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" > </uses-permission> <uses-permission android:name="android.permission.INTERNET" > </uses-permission> <uses-permission android:name="android.permission.ACCES_MOCK_LOCATION" > </uses-permission> <!-- <uses-permission android:name="android.permission.WRITE_APN_SETTINGS"></uses-permission> --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" > </uses-permission> <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" /> <uses-permission android:name="android.permission.WAKE_LOCK" > </uses-permission> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_GPS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.SEND_SMS" /> <!-- SDK1.5需要android.permission.GET_TASKS許可權判斷本程式是否為當前啟動並執行應用? --> <uses-permission android:name="android.permission.GET_TASKS" /> <uses-permission android:name="android.permission.CAMERA" > </uses-permission> <uses-permission android:name="android.permission.RECORD_AUDIO" > </uses-permission> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" > </uses-permission> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- 來電消音 --> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" > </uses-permission> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
都拷到我們自己的AndroidManifest.xml中。(拷貝,粘貼總會了吧)
如果要用到定位服務的話,還要把
<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" > </service>
這個服務加上,開始的時候沒注意,老是定位不到自己所處的方位,加上這個就OK了,百度這方面做得還是不錯的。定位精度也不錯。
好了,都加好了,接下來就是編碼了
首先要定義一個主要的程式運行環境,這裡Demo裡有一個DemoApplication.java,這個類繼承了Application,一般寫程式都會在程式啟動的時候載入一些資料或配置,都可以寫在這個類裡,而且這個類作為程式的主運行類,只要程式不退出,這個類是不會被銷毀的,所以可以把一些全域的方法也寫在這個類裡。
private static DemoApplication mInstance = null; //單例 public boolean m_bKeyRight = true; BMapManager mBMapManager = null; public static final String strKey = "請輸入你的key";@Override public void onCreate() { super.onCreate();mInstance = this;initEngineManager(this); //初始化百度引擎}@Override//建議在您app的退出之前調用mapadpi的destroy()函數,避免重複初始化帶來的時間消耗public void onTerminate() {// TODO Auto-generated method stub if (mBMapManager != null) { mBMapManager.destroy(); mBMapManager = null; }super.onTerminate();}public void initEngineManager(Context context) { if (mBMapManager == null) { mBMapManager = new BMapManager(context); } if (!mBMapManager.init(strKey,new MyGeneralListener())) { Toast.makeText(DemoApplication.getInstance().getApplicationContext(), "BMapManager 初始化錯誤!", Toast.LENGTH_LONG).show(); }}public static DemoApplication getInstance() {return mInstance;}// 常用事件監聽,用來處理通常的網路錯誤,授權驗證錯誤等 static class MyGeneralListener implements MKGeneralListener { @Override public void onGetNetworkState(int iError) { if (iError == MKEvent.ERROR_NETWORK_CONNECT) { Toast.makeText(DemoApplication.getInstance().getApplicationContext(), "您的網路出錯啦!", Toast.LENGTH_LONG).show(); } else if (iError == MKEvent.ERROR_NETWORK_DATA) { Toast.makeText(DemoApplication.getInstance().getApplicationContext(), "輸入正確的檢索條件!", Toast.LENGTH_LONG).show(); } // ... } @Override public void onGetPermissionState(int iError) { if (iError == MKEvent.ERROR_PERMISSION_DENIED) { //授權Key錯誤: Toast.makeText(DemoApplication.getInstance().getApplicationContext(), "請在 DemoApplication.java檔案輸入正確的授權Key!", Toast.LENGTH_LONG).show(); DemoApplication.getInstance().m_bKeyRight = false; } } }
這裡DemoApplication裡的代碼,不多,我們可以將代碼拷貝到我們自己的Application裡,我們也可以把我們的定位程式寫在這裡,這樣我們程式一啟動,就可以定位到我們的位置。(如果再進一步,啟動的時候就開啟一個Service,就可以不停地把我們的座標發送到我們自己的伺服器了,這樣你的一舉一動就都在伺服器記錄了)呵呵,這是後話,當然,我個人是不建議這樣做的。
接下來就是百度地圖Activity了
這裡百度地圖Activity就是繼承了普通的Activity,這裡需要說一下的是,現在Google建議開發程式的時候盡量用Fragment,但我試了老半天,也沒辦法把百度Activity放入Fragment,可能是我的水平還有限,呵呵,如果可以將百度地圖做為一個Fragment的話,那就可以更靈活地使用百度地圖了。希望百度地圖在以後能改進,Google地圖倒是用的Fragment。這個我們以後再說。
在Activity的onCreate裡
DemoApplication app = (DemoApplication)this.getApplication(); if (app.mBMapManager == null) { app.mBMapManager = new BMapManager(this); app.mBMapManager.init(DemoApplication.strKey,new DemoApplication.MyGeneralListener()); } setContentView(R.layout.activity_itemizedoverlay); // 取得地圖控制項 mMapView = (MapView) findViewById(R.id.bmapView); mMapView.setLongClickable(true); //設定地圖顯示放大縮小控制項 mMapView.setBuiltInZoomControls(true); // 取得地圖控制器 mMapController=mMapView.getController(); // 設定地圖中心 mMapController.setCenter(INIT_POINT); // INIT_POINT = new GeoPoint((int) (31.57942800 * 1e6), (int) (120.30795300 * 1e6)); //這裡初始化了顯示中心,一啟動就以當前位置為中心顯示地圖 //設定地圖顯示層級,一般13,14就是顯示道路名稱了 mMapController.setZoom(14); //地圖可以單擊 mMapController.enableClick(true);
這樣基本地圖就能顯示出來了,接下來要加入圖層,其實地圖上顯示的資訊都是往地圖層上加圖層,也不是特別深奧的知識。
這裡我們盡量用非同步來計算座標,載入圖層。
/** * 將資訊加入圖層 * * @param list * @return */private Overlay getOverlay(List<Info> list) {// 標記圖片Drawable dr = getResources().getDrawable(R.drawable.icon_marka);Overlay ov = new Overlay(dr, this);int lat, lon;Info item;for (Info info : list) {lat = (int) (info.getLat() * 1e6);lon = (int) (info.getLon() * 1e6);item = new InfoItem(new GeoPoint(lat, lon),info.getName(), info.getAddress(),info.getsex(),info.getNum());item.setMarker(dr);ov.addItem(item);}return ov;}
這裡將從網路擷取的列錶轉換成Overlay層,然後將擷取的層加入mapView中
mMapView.getOverlays().add(overlay);
這個層類在這裡給出
static class Overlay extends ItemizedOverlay<InfoItem> {public List<InfoItem> mGeoList = new ArrayList<InfoItem>();private Context mContext = null;static PopupOverlay pop = null;private View popView = null;private InfoItem mItem;private Bitmap map;static class ItemView { // 自訂控制項集合public TextView NameTextView;public TextView SexTextView;public TextView NumTextView;public TextView AddressTextView;} public Overlay(Drawable marker, Context context) {super(marker);this.mContext = context;pop = new PopupOverlay(BaiduMap.mMapView, new PopupClickListener() {@Overridepublic void onClickedPopup(int index) {}});populate();} //表徵圖單擊事件protected boolean onTap(int index) {mOverlayItem = mGeoList.get(index);ItemView item = null;if (popView == null) {popView = LayoutInflater.from(mContext).inflate(R.layout.popup_view, null);// 擷取要轉換的View資源item = new ItemView();item.TitleTextView = (TextView) popView.findViewById(R.id.pop_title);item.TotalNumTextView = (TextView) popView.findViewById(R.id.pop_num);item.CurrentNumTextView = (TextView) popView.findViewById(R.id.pop_leftnum);item.AddressTextView = (TextView) popView.findViewById(R.id.pop_address);popView.setTag(item);} else {item = (ItemView) popView.getTag();}item.NameTextView.setText(mItem.getName());// 將每個點的Title在彈窗中以文本形式顯示出來item.SexTextView.setText(mItem.getSex());item.NumTextView.setText(mItem.getNum());item.AddressTextView.setText(mItem.getSnippet());//圖片大小,將View轉換成Bitmapmap = convertViewToBitmap(popView, 300, 200);// 顯示poppop.showPopup(map, mGeoList.get(index).getPoint(), 32);map.recycle(); //回收資源map = null;return true;}public boolean onTap(GeoPoint pt, MapView mapView) {if (pop != null) {pop.hidePop();}super.onTap(pt, mapView);return false;}@Overrideprotected InfoItem createItem(int i) {return mGeoList.get(i);}@Overridepublic int size() {return mGeoList.size();}public void addItem(InfoItem item) {mGeoList.add(item);populate();}public void removeItem(int index) {mGeoList.remove(index);populate();}}/** * 把View繪製到Bitmap上 * * @param view * 需要繪製的View * @param width * 該View的寬度 * @param height * 該View的高度 * @return 返回Bitmap對象 */public static Bitmap convertViewToBitmap(View view, int width, int height) {int widthSpec = View.MeasureSpec.makeMeasureSpec(width,View.MeasureSpec.EXACTLY);int heightSpec = View.MeasureSpec.makeMeasureSpec(height,View.MeasureSpec.EXACTLY);view.measure(widthSpec, heightSpec);view.layout(0, 0, width, height);// Bitmap bitmap = Bitmap.createBitmap(width, height,// Bitmap.Config.ARGB_8888);// Canvas canvas = new Canvas(bitmap);// view.draw(canvas);view.clearFocus();view.setPressed(false);boolean willNotCache = view.willNotCacheDrawing();view.setWillNotCacheDrawing(false);// Reset the drawing cache background color to fully transparent// for the duration of this operationint color = view.getDrawingCacheBackgroundColor();view.setDrawingCacheBackgroundColor(0);if (color != 0) {view.destroyDrawingCache();}view.buildDrawingCache();Bitmap cacheBitmap = view.getDrawingCache();if (cacheBitmap == null) {Log.e(TAG, "failed getViewBitmap(" + view + ")",new RuntimeException());return null;}Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);// Restore the viewview.destroyDrawingCache();view.setWillNotCacheDrawing(willNotCache);view.setDrawingCacheBackgroundColor(color);return bitmap;}
這裡要注意的是百度的PopupOverlay只能顯示Bitmap,所以我們要把View轉換成Bitmap,Bitmap使用完以後要及時回收,如果圖片多的話會造成out of memory的異常。
現在應該就可以顯示如上的效果了,源碼因為是客戶的,所以沒辦法給出,但如果有什麼問題的話,可以給我留言。謝謝。