Android 百度地图开发(三)

实现比例尺功能和替换自带的缩放组件


ScaleView是比例尺控件。ZoomControlView是缩放控件,MainActivity就是我们的主界面了


先看下ZoomControlView类。代码例如以下

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.baidumapdemo;  
  2.   
  3. import com.baidu.mapapi.map.MapView;  
  4.   
  5. import android.content.Context;  
  6. import android.util.AttributeSet;  
  7. import android.view.LayoutInflater;  
  8. import android.view.View;  
  9. import android.widget.Button;  
  10. import android.widget.RelativeLayout;  
  11. import android.view.View.OnClickListener;  
  12.   
  13. public class ZoomControlView extends RelativeLayout implements OnClickListener{  
  14.     private Button mButtonZoomin;  
  15.     private Button mButtonZoomout;  
  16.     private MapView mapView;  
  17.     private int maxZoomLevel;  
  18.     private int minZoomLevel;  
  19.       
  20.     public ZoomControlView(Context context, AttributeSet attrs) {  
  21.         this(context, attrs, 0);  
  22.     }  
  23.   
  24.     public ZoomControlView(Context context, AttributeSet attrs, int defStyle) {  
  25.         super(context, attrs, defStyle);  
  26.         init();  
  27.     }  
  28.   
  29.       
  30.     private void init() {  
  31.         View view = LayoutInflater.from(getContext()).inflate(R.layout.zoom_controls_layout, null);  
  32.         mButtonZoomin = (Button) view.findViewById(R.id.zoomin);  
  33.         mButtonZoomout = (Button) view.findViewById(R.id.zoomout);  
  34.         mButtonZoomin.setOnClickListener(this);  
  35.         mButtonZoomout.setOnClickListener(this);  
  36.         addView(view);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void onClick(View v) {  
  41.         if(mapView == null){  
  42.             throw new NullPointerException("you can call setMapView(MapView mapView) at first");  
  43.         }  
  44.         switch (v.getId()) {  
  45.         case R.id.zoomin:{  
  46.             mapView.getController().zoomIn();  
  47.             break;  
  48.         }  
  49.         case R.id.zoomout:{  
  50.             mapView.getController().zoomOut();  
  51.             break;  
  52.         }  
  53.         }  
  54.     }  
  55.   
  56.     /** 
  57.      * 与MapView设置关联 
  58.      * @param mapView 
  59.      */  
  60.     public void setMapView(MapView mapView) {  
  61.         this.mapView = mapView;  
  62.         // 获取最大的缩放级别  
  63.         maxZoomLevel = mapView.getMaxZoomLevel();  
  64.         // 获取最大的缩放级别  
  65.         minZoomLevel = mapView.getMinZoomLevel();  
  66.     }  
  67.       
  68.       
  69.     /** 
  70.      * 依据MapView的缩放级别更新缩放button的状态。当达到最大缩放级别,设置mButtonZoomin 
  71.      * 为不能点击,反之设置mButtonZoomout 
  72.      * @param level 
  73.      */  
  74.     public void refreshZoomButtonStatus(int level){  
  75.         if(mapView == null){  
  76.             throw new NullPointerException("you can call setMapView(MapView mapView) at first");  
  77.         }  
  78.         if(level > minZoomLevel && level < maxZoomLevel){  
  79.             if(!mButtonZoomout.isEnabled()){  
  80.                 mButtonZoomout.setEnabled(true);  
  81.             }  
  82.             if(!mButtonZoomin.isEnabled()){   
  83.                 mButtonZoomin.setEnabled(true);  
  84.             }  
  85.         }  
  86.         else if(level == minZoomLevel ){  
  87.             mButtonZoomout.setEnabled(false);  
  88.         }  
  89.         else if(level == maxZoomLevel){  
  90.             mButtonZoomin.setEnabled(false);  
  91.         }  
  92.     }  
  93.   
  94. }  


这个类封装好了地图的缩放功能,里面主要是两个button,一个button是放大MapView。当放大到了MapView的最大缩放级别,设置此button的Enable为false,还有一个button缩小MapView,当缩小到了MapView最小的缩放级别,设置此button的Enable为false。refreshZoomButtonStatus()方法就是实现了此功能,我们依据MapView的缩放级别来更新button的状态,我们还必须调用setMapView(MapView mapView)方法来设置ZoomControlView与MapView关联,ZoomControlView的布局文件zoom_controls_layout我就不贴出来了,里面就两个button,然后给button设置背景选择器seletor


ScaleView类的代码例如以下

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <span style="font-family:System;font-size:12px;">package com.example.baidumapdemo;  
  2.   
  3. import com.baidu.mapapi.map.MapView;  
  4. import com.baidu.platform.comapi.basestruct.GeoPoint;  
  5.   
  6. import android.annotation.SuppressLint;  
  7. import android.content.Context;  
  8. import android.graphics.Bitmap;  
  9. import android.graphics.BitmapFactory;  
  10. import android.graphics.Canvas;  
  11. import android.graphics.Color;  
  12. import android.graphics.NinePatch;  
  13. import android.graphics.Paint;  
  14. import android.graphics.Rect;  
  15. import android.graphics.Typeface;  
  16. import android.util.AttributeSet;  
  17. import android.view.View;  
  18.   
  19. public class ScaleView extends View {  
  20.     private Paint mPaint;  
  21.     /** 
  22.      * 比例尺的宽度 
  23.      */  
  24.     private int scaleWidth;  
  25.     /** 
  26.      * 比例尺的高度 
  27.      */  
  28.     private int scaleHeight = 4;  
  29.     /** 
  30.      * 比例尺上面字体的颜色 
  31.      */  
  32.     private int textColor = Color.BLACK;  
  33.     /** 
  34.      * 比例尺上边的字体 
  35.      */  
  36.     private String text;  
  37.     /** 
  38.      * 字体大小 
  39.      */  
  40.     private int textSize = 16;  
  41.     /** 
  42.      * 比例尺与字体间的距离 
  43.      */  
  44.     private int scaleSpaceText = 8;  
  45.     /** 
  46.      * 百度地图最大缩放级别 
  47.      */  
  48.     private static final int MAX_LEVEL = 19;  
  49.     /** 
  50.      * 各级比例尺分母值数组 
  51.      */  
  52.     private static final int[] SCALES = {205010020050010002000,  
  53.             5000100002000025000500001000002000005000001000000,  
  54.             2000000 };  
  55.     /** 
  56.      * 各级比例尺上面的文字数组 
  57.      */  
  58.     private static final String[] SCALE_DESCS = { "20米""50米""100米""200米",  
  59.             "500米""1公里""2公里""5公里""10公里""20公里""25公里""50公里",  
  60.             "100公里""200公里""500公里""1000公里""2000公里" };  
  61.       
  62.     private MapView mapView;  
  63.       
  64.       
  65.   
  66.     /** 
  67.      * 与MapView设置关联 
  68.      * @param mapView 
  69.      */  
  70.     public void setMapView(MapView mapView) {  
  71.         this.mapView = mapView;  
  72.     }  
  73.   
  74.     public ScaleView(Context context) {  
  75.         this(context, null);  
  76.     }  
  77.       
  78.     public ScaleView(Context context, AttributeSet attrs) {  
  79.         this(context, attrs, 0);  
  80.     }  
  81.   
  82.     public ScaleView(Context context, AttributeSet attrs, int defStyle) {  
  83.         super(context, attrs, defStyle);  
  84.         mPaint = new Paint();  
  85.     }  
  86.   
  87.       
  88.     /** 
  89.      * 绘制上面的文字和以下的比例尺,由于比例尺是.9.png,我们须要利用drawNinepath方法绘制比例尺 
  90.      */  
  91.     @SuppressLint("DrawAllocation")  
  92.     @Override  
  93.     protected void onDraw(Canvas canvas) {  
  94.         super.onDraw(canvas);  
  95.           
  96.         int width = scaleWidth;  
  97.           
  98.         mPaint.setColor(textColor);  
  99.         mPaint.setAntiAlias(true);  
  100.         mPaint.setTextSize(textSize);  
  101.         mPaint.setTypeface(Typeface.DEFAULT_BOLD);  
  102.         float textWidth = mPaint.measureText(text);  
  103.           
  104.         canvas.drawText(text, (width - textWidth) / 2, textSize, mPaint);  
  105.           
  106.         Rect scaleRect = new Rect(0, textSize + scaleSpaceText, scaleWidth, textSize + scaleSpaceText + scaleHeight);  
  107.         drawNinepath(canvas, R.drawable.icon_scale, scaleRect);  
  108.     }  
  109.       
  110.     /** 
  111.      * 手动绘制.9.png图片 
  112.      * @param canvas 
  113.      * @param resId 
  114.      * @param rect 
  115.      */  
  116.     private void drawNinepath(Canvas canvas, int resId, Rect rect){    
  117.         Bitmap bmp= BitmapFactory.decodeResource(getResources(), resId);    
  118.         NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);    
  119.         patch.draw(canvas, rect);    
  120.     }  
  121.   
  122.       
  123.   
  124.     /** 
  125.      * 測量ScaleView的方法。 
  126.      */  
  127.     @Override  
  128.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  129.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  130.         int widthSize = getWidthSize(widthMeasureSpec);  
  131.         int heightSize = getHeightSize(heightMeasureSpec);  
  132.         setMeasuredDimension(widthSize, heightSize);  
  133.     }  
  134.       
  135.     /** 
  136.      * 測量ScaleView的宽度 
  137.      * @param widthMeasureSpec 
  138.      * @return 
  139.      */  
  140.     private int getWidthSize(int widthMeasureSpec){  
  141.         return MeasureSpec.getSize(widthMeasureSpec);  
  142.     }  
  143.       
  144.     /** 
  145.      * 測量ScaleView的高度 
  146.      * @param widthMeasureSpec 
  147.      * @return 
  148.      */  
  149.     private int getHeightSize(int heightMeasureSpec){  
  150.         int mode = MeasureSpec.getMode(heightMeasureSpec);  
  151.         int height = 0;  
  152.         switch (mode) {  
  153.         case MeasureSpec.AT_MOST:  
  154.             height = textSize + scaleSpaceText + scaleHeight;  
  155.             break;  
  156.         case MeasureSpec.EXACTLY:{  
  157.             height = MeasureSpec.getSize(heightMeasureSpec);  
  158.             break;  
  159.         }  
  160.         case MeasureSpec.UNSPECIFIED:{  
  161.             height = Math.max(textSize + scaleSpaceText + scaleHeight, MeasureSpec.getSize(heightMeasureSpec));  
  162.             break;  
  163.         }  
  164.         }  
  165.           
  166.         return height;  
  167.     }  
  168.       
  169.     /** 
  170.      * 依据缩放级别,得到相应比例尺在SCALES数组中的位置(索引) 
  171.      * @param zoomLevel 
  172.      * @return 
  173.      */  
  174.     private static int getScaleIndex(int zoomLevel) {  
  175.         return MAX_LEVEL - zoomLevel;  
  176.     }  
  177.   
  178.     /** 
  179.      * 依据缩放级别。得到相应比例尺 
  180.      *  
  181.      * @param zoomLevel 
  182.      * @return 
  183.      */  
  184.     public static int getScale(int zoomLevel) {  
  185.         return SCALES[getScaleIndex(zoomLevel)];  
  186.     }  
  187.   
  188.     /** 
  189.      *  依据缩放级别,得到相应比例尺文字 
  190.      * @param zoomLevel 
  191.      * @return 
  192.      */  
  193.     public static String getScaleDesc(int zoomLevel) {  
  194.         return SCALE_DESCS[getScaleIndex(zoomLevel)];  
  195.     }  
  196.   
  197.       
  198.     /** 
  199.      * 依据地图当前中心位置的纬度。当前比例尺,得出比例尺图标应该显示多长(多少像素) 
  200.      * @param map 
  201.      * @param scale 
  202.      * @return 
  203.      */  
  204.     public static int meterToPixels(MapView map, int scale) {  
  205.         // 得到当前中心位置对象  
  206.         GeoPoint geoPoint = map.getMapCenter();  
  207.         // 得到当前中心位置纬度  
  208.         double latitude = geoPoint.getLatitudeE6() / 1E6;  
  209.         // 得到象素数,比方当前比例尺是1/10000。比方scale=10000,相应在该纬度应在地图中绘多少象素  
  210.         // 參考http://rainbow702.iteye.com/blog/1124244  
  211.         return (int) (map.getProjection().metersToEquatorPixels(scale) / (Math  
  212.                 .cos(Math.toRadians(latitude))));  
  213.           
  214.           
  215.     }  
  216.   
  217.     /** 
  218.      * 设置比例尺的宽度 
  219.      * @param scaleWidth 
  220.      */  
  221.     public  void setScaleWidth(int scaleWidth) {  
  222.         this.scaleWidth = scaleWidth;  
  223.     }  
  224.   
  225.     /** 
  226.      * 设置比例尺的上面的 text 比如 200公里 
  227.      * @param text 
  228.      */  
  229.     private void setText(String text) {  
  230.         this.text = text;  
  231.     }  
  232.   
  233.     /** 
  234.      * 设置字体大小 
  235.      * @param textSize 
  236.      */  
  237.     public void setTextSize(int textSize) {  
  238.         this.textSize = textSize;  
  239.         invalidate();  
  240.     }  
  241.       
  242.       
  243.     /** 
  244.      * 依据缩放级别更新ScaleView的文字以及比例尺的长度 
  245.      * @param level 
  246.      */  
  247.     public void refreshScaleView(int level) {  
  248.         if(mapView == null){  
  249.             throw new NullPointerException("you can call setMapView(MapView mapView) at first");  
  250.         }  
  251.         setText(getScaleDesc(level));  
  252.         setScaleWidth(meterToPixels(mapView, getScale(level)));  
  253.         invalidate();  
  254.     }  
  255.   
  256. }</span>  

ScaleView是比例尺控件类,主要是提供比例尺的绘制功能,在onDraw(Canvas canvas)的方法中绘制上面的距离和以下的比例尺长度。这里面要说下drawNinepath()方法,由于我们的比例尺图片是.9.png格式的,保证图片拉伸状态下不变形,假设用寻常绘制一般图片的方法会报ClassCastException。所以我们能够利用drawNinepath()来手动绘制.9.png格式的图片。我们比例尺的长度是变化的。我们须要将以米为计量单位的距离(沿赤道)在当前缩放水平下转换到一个以像素(水平)为计量单位的距离,meterToPixels()方法依据我们MapView当前的缩放级别,得出比例尺图标应该显示多长。refreshScaleView()是用来更新ScaleView的,更新上面的字符串和以下比例尺的长度,当然我们也必须调用setMapView(MapView mapView)方法来设置ScaleView与MapView关联


接下来我们就来使用我们的比例尺控件和缩放控件了,先看下布局

[html] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <?

    xml version="1.0" encoding="utf-8"?

    >  

  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent" >  
  5.   
  6.     <com.baidu.mapapi.map.MapView  
  7.         android:id="@+id/bmapView"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="fill_parent"  
  10.         android:clickable="true" />  
  11.   
  12.     <com.example.baidumapdemo.ZoomControlView  
  13.         android:id="@+id/ZoomControlView"  
  14.         android:layout_width="wrap_content"  
  15.         android:layout_height="wrap_content"  
  16.         android:layout_alignParentBottom="true"  
  17.         android:layout_alignParentRight="true"  
  18.         android:layout_marginBottom="20.0dip"  
  19.         android:layout_marginRight="5.0dip"/>  
  20.   
  21.     <com.example.baidumapdemo.ScaleView  
  22.         android:id="@+id/scaleView"  
  23.         android:layout_width="wrap_content"  
  24.         android:layout_height="wrap_content"  
  25.         android:layout_alignParentBottom="true"  
  26.         android:layout_alignParentLeft="true"  
  27.         android:layout_marginBottom="40dp"  
  28.         android:layout_marginLeft="20dp" />  
  29.   
  30. </RelativeLayout>  
主界面MainActivity的代码例如以下

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. package com.example.baidumapdemo;  
  2.   
  3. import android.app.Activity;  
  4. import android.graphics.Bitmap;  
  5. import android.os.Bundle;  
  6. import android.widget.Toast;  
  7.   
  8. import com.baidu.mapapi.BMapManager;  
  9. import com.baidu.mapapi.MKGeneralListener;  
  10. import com.baidu.mapapi.map.MKEvent;  
  11. import com.baidu.mapapi.map.MKMapViewListener;  
  12. import com.baidu.mapapi.map.MapController;  
  13. import com.baidu.mapapi.map.MapPoi;  
  14. import com.baidu.mapapi.map.MapView;  
  15. import com.baidu.platform.comapi.basestruct.GeoPoint;  
  16.   
  17. public class MainActivity extends Activity{  
  18.     private BMapManager mBMapManager;  
  19.     /** 
  20.      * MapView 是地图主控件 
  21.      */  
  22.     private MapView mMapView = null;  
  23.     /** 
  24.      * 用MapController完毕地图控制 
  25.      */  
  26.     private MapController mMapController = null;  
  27.       
  28.     private ScaleView mScaleView;  
  29.     private ZoomControlView mZoomControlView;  
  30.       
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.           
  35.         //使用地图sdk前需先初始化BMapManager,这个必须在setContentView()先初始化  
  36.         mBMapManager = new BMapManager(this);  
  37.           
  38.         //第一个參数是API key,  
  39.         //第二个參数是经常使用事件监听,用来处理通常的网络错误,授权验证错误等,你也能够不加入这个回调接口  
  40.         mBMapManager.init("CC61ac7527b65c95899608810873b173"new MKGeneralListener() {  
  41.               
  42.             //授权错误的时候调用的回调函数  
  43.             @Override  
  44.             public void onGetPermissionState(int iError) {  
  45.                 if (iError ==  MKEvent.ERROR_PERMISSION_DENIED) {  
  46.                     Toast.makeText(getApplication(), "API Key错误。请检查。",  
  47.                             Toast.LENGTH_LONG).show();  
  48.                 }  
  49.             }  
  50.               
  51.             //一些网络状态的错误处理回调函数  
  52.             @Override  
  53.             public void onGetNetworkState(int iError) {  
  54.                 if (iError == MKEvent.ERROR_NETWORK_CONNECT) {  
  55.                     Toast.makeText(getApplication(), "您的网络出错啦!",  
  56.                         Toast.LENGTH_LONG).show();  
  57.                 }  
  58.             }  
  59.         });  
  60.           
  61.         setContentView(R.layout.activity_main);  
  62.         mMapView = (MapView) findViewById(R.id.bmapView);  
  63.         //隐藏自带的地图缩放控件  
  64.         mMapView.setBuiltInZoomControls(false);  
  65.           
  66.         mScaleView = (ScaleView) findViewById(R.id.scaleView);  
  67.         mScaleView.setMapView(mMapView);  
  68.         mZoomControlView = (ZoomControlView) findViewById(R.id.ZoomControlView);  
  69.         mZoomControlView.setMapView(mMapView);  
  70.           
  71.           
  72.         //地图显示事件监听器。 该接口监听地图显示事件,用户须要实现该接口以处理对应事件。  
  73.         mMapView.regMapViewListener(mBMapManager, new MKMapViewListener() {  
  74.               
  75.             @Override  
  76.             public void onMapMoveFinish() {  
  77.                  refreshScaleAndZoomControl();  
  78.             }  
  79.               
  80.             @Override  
  81.             public void onMapLoadFinish() {  
  82.                   
  83.             }  
  84.               
  85.               
  86.               
  87.             /** 
  88.              * 动画结束时会回调此消息.我们在此方法里面更新缩放button的状态 
  89.              */  
  90.             @Override  
  91.             public void onMapAnimationFinish() {  
  92.                  refreshScaleAndZoomControl();  
  93.             }  
  94.               
  95.             @Override  
  96.             public void onGetCurrentMap(Bitmap arg0) {  
  97.                   
  98.             }  
  99.               
  100.             @Override  
  101.             public void onClickMapPoi(MapPoi arg0) {  
  102.                   
  103.             }  
  104.         });  
  105.           
  106.         //获取地图控制器  
  107.         mMapController = mMapView.getController();  
  108.         //设置地图是否响应点击事件  .  
  109.         mMapController.enableClick(true);  
  110.         //设置地图缩放级别  
  111.         mMapController.setZoom(14);  
  112.           
  113.         refreshScaleAndZoomControl();  
  114.           
  115.         //保存精度和纬度的类,  
  116.         GeoPoint p = new GeoPoint((int)(22.547923 * 1E6), (int)(114.067368 * 1E6));  
  117.         //设置p地方为中心点  
  118.         mMapController.setCenter(p);  
  119.           
  120.     }  
  121.   
  122.       
  123.       
  124.     private void refreshScaleAndZoomControl(){  
  125.         //更新缩放button的状态  
  126.         mZoomControlView.refreshZoomButtonStatus(Math.round(mMapView.getZoomLevel()));  
  127.         mScaleView.refreshScaleView(Math.round(mMapView.getZoomLevel()));  
  128.     }  
  129.       
  130.       
  131.     @Override  
  132.     protected void onResume() {  
  133.         //MapView的生命周期与Activity同步,当activity挂起时需调用MapView.onPause()  
  134.         mMapView.onResume();  
  135.         super.onResume();  
  136.     }  
  137.   
  138.   
  139.   
  140.     @Override  
  141.     protected void onPause() {  
  142.         //MapView的生命周期与Activity同步。当activity挂起时需调用MapView.onPause()  
  143.         mMapView.onPause();  
  144.         super.onPause();  
  145.     }  
  146.   
  147.     @Override  
  148.     protected void onDestroy() {  
  149.         //MapView的生命周期与Activity同步。当activity销毁时需调用MapView.destroy()  
  150.         mMapView.destroy();  
  151.           
  152.         //退出应用调用BMapManager的destroy()方法  
  153.         if(mBMapManager != null){  
  154.             mBMapManager.destroy();  
  155.             mBMapManager = null;  
  156.         }  
  157.           
  158.         super.onDestroy();  
  159.           
  160.     }  
  161.   
  162. }  

主界面的代码还是比較简单的。主要是利用MapView的regMapViewListener()来注冊地图显示事件监听器。

该接口监听地图显示事件。用户须要实现该接口以处理对应事件,分别在回调方法onMapAnimationFinish()和onMapMoveFinish()来更新ZoomControlView和ScaleView的一些状态

在执行之前须要在Manifest中增加相相应的权限问题

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    
  2. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />    
  3. <uses-permission android:name="android.permission.INTERNET" />    
  4. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />    
  5. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />    
  6. <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />    
  7. <uses-permission android:name="android.permission.READ_PHONE_STATE" />  

执行结果



好了,今天的解说到此结束。有疑问的朋友请在以下留言!




posted @ 2015-12-31 16:40  hrhguanli  阅读(402)  评论(0编辑  收藏  举报