在我们进行地图相关开发时候,避免不了要绘制比例尺。在百度,高德的地图API里都提供了比例尺控件,但是ArcGIS for Android里并没有提供。不过没关系,我们可以自己绘制一个比例尺来。
在绘制比例尺前,我们先了解几个概念:
- PPI,Pixels Per Inch的所写,表示的是每英寸所拥有的像素数目;
- PX,像素,表示图像中的一个最小单位;
- DPI,Dots Per Inch,每英寸点数,即图像密度;
- .9.PNG,Android开发里面的一种特殊的图片,这种格式的图片通过ADT自带的编辑工具生成,使用九宫格切分的方法,使图片支持在Android环境下的自适应展示。即这种类型图片在Android里无论怎样拉伸缩小都不失真。
其中PPI和DPI在实际生活中的定义是不太一样的,而在Android里,他们的含义却是相似的。单独把DPI拿出来主要是Android里有个方法可以分别获取到屏幕X轴和Y轴的像素密度。
.9.PNG格式的图片不失真,正好适合我们做来做比例尺图片。
好了,我们好绘制一个比例尺,需要做些什么呢?
首先,我们得知道当前地图比例,这个参数可以通过MapView.getScale来获取;
其次,我们要根据当前地图比例绘制一个比例尺。绘制的方案有两种,一个是固定尺子长度,根据当前地图比例更换比例尺的比,比如1:2000,1:3000等;另一种是固定一些比例单位,比如1:50000后就是1:20000,然后比例尺的长度根据实际长度会做一定伸缩。这里我采用第二种,因为第一种根据实际比例往往比例尺的比值不是整数,并且在较大比例时候显示位数较长,四舍五入又会失去精度。
最后,是监听地图放大缩小事件,并作出响应改变比例尺。
方法就是这么简单,那就实际开动吧。
第一步,实例化地图,加载图层:
[java] view plain
class="tracking-ad" data-mod="popu_168"> copy
- mMapView=(MapView)findViewById(R.id.mapview);
- mMapScaleView=(MapScaleView)findViewById(R.id.scaleView);
- String path= StorageUtil.getSDCardRootPath(getApplicationContext());
- ArcGISLocalTiledLayer layer=new ArcGISLocalTiledLayer(path);
- mMapView.addLayer(layer,0);
- mMapScaleView.setMapView(mMapView);
- ArcGISRuntime.setClientId(System.clint);
- mMapView.setMapBackground(0xFAFAFA, 0xffffff, 0.0f, 0.0f);
第二步,自定义View,绘制比例尺:
初始化自定义View:
[java] view plain
copy
- public MapScaleView(Context context) {
- this(context, null);
- this.context=context;
- this.initVariables();
- }
-
- public MapScaleView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- this.context=context;
- this.initVariables();
- }
-
- public MapScaleView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- this.context=context;
- this.initVariables();
- }
初始化自定义View的参数:
[java] view plain
copy
- private void initVariables(){
- scaleWidth=104;
- scaleHeight=8;
- textColor= Color.BLACK;
- text="20公里";
- textSize=18;
- scaleSpaceText=8;
- mPaint = new Paint();
- }
重写onMearsure()方法:
[java] view plain
copy
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int widthSize = getWidthSize(widthMeasureSpec);
- int heightSize = getHeightSize(heightMeasureSpec);
- setMeasuredDimension(widthSize, heightSize);
- }
获取自定义View的宽和高:
[java] view plain
copy
- **
- * 测量ScaleView的宽度
- * @param widthMeasureSpec
- * @return
- */
- private int getWidthSize(int widthMeasureSpec){
- return MeasureSpec.getSize(widthMeasureSpec);
- }
-
- private int getHeightSize(int heightMeasureSpec){
- int mode = MeasureSpec.getMode(heightMeasureSpec);
- int height = 0;
- switch (mode) {
- case MeasureSpec.AT_MOST:
- height = textSize + scaleSpaceText + scaleHeight;
- break;
- case MeasureSpec.EXACTLY:{
- height = MeasureSpec.getSize(heightMeasureSpec);
- break;
- }
- case MeasureSpec.UNSPECIFIED:{
- height = Math.max(textSize + scaleSpaceText + scaleHeight, MeasureSpec.getSize(heightMeasureSpec));
- break;
- }
- }
-
- return height;
- }
重写onDraw()方法,绘制比例尺:
[java] view plain
copy
- **
- * 绘制上面的文字和下面的比例尺,因为比例尺是.9.png,我们需要利用drawNinepath方法绘制比例尺
- */
- @SuppressLint("DrawAllocation")
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- int width = scaleWidth ;
- mPaint.setColor(textColor);
- mPaint.setAntiAlias(true);
- mPaint.setTextSize(textSize);
- mPaint.setTypeface(Typeface.DEFAULT_BOLD);
- float textWidth = mPaint.measureText(text);
- canvas.drawText(text, (width - textWidth) / 2, textSize, mPaint);
- Rect scaleRect = new Rect(0, textSize + scaleSpaceText, width, textSize + scaleSpaceText + scaleHeight);
- drawNinepath(canvas, R.drawable.icon_scale, scaleRect);
- }
绘制.9.PNG图片:
[java] view plain
copy
- private void drawNinepath(Canvas canvas, int resId, Rect rect){
- Bitmap bmp= BitmapFactory.decodeResource(getResources(), resId);
- NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);
- patch.draw(canvas, rect);
- }
接下来就是根据获取的地图比例,绘制比例尺,更新比例尺的单位以及绘制它的长度,这里我按照2、5、1的进制选取了比例尺的单位:
[java] view plain
copy
- public void refreshScaleView() {
- if(mapView == null){
- throw new NullPointerException("you can call setMapView(MapView mapView) at first");
- }
- double scale=this.mapView.getScale()/100;
- double ppi=getPPIOfDevice();
- if(scale>0&&scale<=20){
- String unit = "20米";
- int scaleWidth=(int)(20*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>20&&scale<=50){
- String unit = "50米";
- int scaleWidth=(int)(50*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>50&&scale<=100){
- String unit = "100米";
- int scaleWidth=(int)(100*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>100&&scale<=200){
- String unit = "200米";
- int scaleWidth=(int)(200*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>200&&scale<=500){
- String unit = "500米";
- int scaleWidth=(int)(500*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>500&&scale<=1000){
- String unit = "1公里";
- int scaleWidth=(int)(1000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>1000&&scale<=2000){
- String unit = "2公里";
- int scaleWidth=(int)(2000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>2000&&scale<=5000){
- String unit = "5公里";
- int scaleWidth=(int)(5000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>5000&&scale<=10000){
- String unit = "10公里";
- int scaleWidth=(int)(10000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>10000&&scale<=20000){
- String unit = "20公里";
- int scaleWidth=(int)(20000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>20000&&scale<=25000){
- String unit = "25公里";
- int scaleWidth=(int)(25000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>25000&&scale<=50000){
- String unit = "50公里";
- int scaleWidth=(int)(50000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>50000&&scale<=100000){
- String unit = "100公里";
- int scaleWidth=(int)(100000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>100000&&scale<=200000){
- String unit = "200公里";
- int scaleWidth=(int)(200000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>200000&&scale<=250000){
- String unit = "250公里";
- int scaleWidth=(int)(250000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>250000&&scale<=500000){
- String unit = "500公里";
- int scaleWidth=(int)(500000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }else if(scale>500000&&scale<=1000000){
- String unit = "1000公里";
- int scaleWidth=(int)(1000000*ppi/2.54/scale);
- setText(unit);
- setScaleWidth(scaleWidth);
- }
-
- invalidate();
- }
其中,获取屏幕PPI的方法如下:首先是用android.view包下的Display类里的getRealSize()方法获取屏幕分辨率;其次是用android.util包下有个DisplayMetrics类来获取密度相关的信息,该类里的xdpi和ydpi参数分别表示屏幕X轴和Y轴的点数密度,用X轴和Y轴的像素除以密度得到X轴和Y轴的真实长度,进而求得屏幕对角线的真实长度;最后用屏幕对角线的像素点数除以屏幕对角线的真实长度,得到屏幕的PPI:
[java] view plain
copy
- private double getPPIOfDevice() {
- Point point = new Point();
- Activity activity=(Activity) context;
- activity.getWindowManager().getDefaultDisplay().getRealSize(point);
- DisplayMetrics dm = getResources().getDisplayMetrics();
- double x = Math.pow(point.x/ dm.xdpi, 2);
- double y = Math.pow(point.y / dm.ydpi, 2);
- double screenInches = Math.sqrt(x + y);
- Double ppi=Math.sqrt(Math.pow(point.x, 2)+Math.pow(point.y, 2))/screenInches;
- return ppi;
- }
源码免费下载地址:http://www.jinhusns.com/Products/Download
第三步:写响应事件
在ArcGIS的MapView有个监听地图大小变化的方法,叫setOnZoomListener(),在里面写响应事件即可:
[java] view plain
copy
- mMapView.setOnZoomListener(new OnZoomListener() {
- @Override
- public void preAction(float v, float v1, double v2) {
-
- }
-
- @Override
- public void postAction(float v, float v1, double v2) {
- mMapScaleView.refreshScaleView();
- }
- });
效果图如下: