在我們進行地圖相關開發時候,避免不了要繪製比例尺。在百度,高德的地圖API里都提供了比例尺控制項,但是ArcGIS for Android里並沒有提供。不過沒關係,我們可以自己繪製一個比例尺來。 在繪製比例尺前,我們先瞭解幾個概念: 其中PPI和DPI在實際生活中的定義是不太一樣的,而在Android ...
在我們進行地圖相關開發時候,避免不了要繪製比例尺。在百度,高德的地圖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 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);
- }
- /**
- * 測量ScaleView的高度
- * @param heightMeasureSpec
- * @return
- */
- 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
- /**
- * 根據縮放級別更新ScaleView的文字以及比例尺的長度
- */
- public void refreshScaleView() {
- if(mapView == null){
- throw new NullPointerException("you can call setMapView(MapView mapView) at first");
- }
- double scale=this.mapView.getScale()/100;//結果單位米,表示圖上1釐米代表*米
- double ppi=getPPIOfDevice();
- if(scale>0&&scale<=20){//換算20米
- String unit = "20米";
- int scaleWidth=(int)(20*ppi/2.54/scale);//ppi為每英尺像素數,ppi/2.54為1釐米的像素數
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>20&&scale<=50){//換算50米
- String unit = "50米";
- int scaleWidth=(int)(50*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>50&&scale<=100){//換算20米
- String unit = "100米";
- int scaleWidth=(int)(100*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>100&&scale<=200){//換算20米
- String unit = "200米";
- int scaleWidth=(int)(200*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>200&&scale<=500){//換算20米
- String unit = "500米";
- int scaleWidth=(int)(500*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>500&&scale<=1000){//換算20米
- String unit = "1公裡";
- int scaleWidth=(int)(1000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>1000&&scale<=2000){//換算20米
- String unit = "2公裡";
- int scaleWidth=(int)(2000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>2000&&scale<=5000){//換算20米
- String unit = "5公裡";
- int scaleWidth=(int)(5000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>5000&&scale<=10000){//換算20米
- String unit = "10公裡";
- int scaleWidth=(int)(10000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>10000&&scale<=20000){//換算20米
- String unit = "20公裡";
- int scaleWidth=(int)(20000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>20000&&scale<=25000){//換算20米
- String unit = "25公裡";
- int scaleWidth=(int)(25000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>25000&&scale<=50000){//換算20米
- String unit = "50公裡";
- int scaleWidth=(int)(50000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>50000&&scale<=100000){//換算20米
- String unit = "100公裡";
- int scaleWidth=(int)(100000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>100000&&scale<=200000){//換算20米
- String unit = "200公裡";
- int scaleWidth=(int)(200000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>200000&&scale<=250000){//換算20米
- String unit = "250公裡";
- int scaleWidth=(int)(250000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>250000&&scale<=500000){//換算20米
- String unit = "500公裡";
- int scaleWidth=(int)(500000*ppi/2.54/scale);
- setText(unit);//更新文字
- setScaleWidth(scaleWidth);//更新比例尺長度
- }else if(scale>500000&&scale<=1000000){//換算20米
- 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) {
- //todo
- }
- @Override
- public void postAction(float v, float v1, double v2) {
- mMapScaleView.refreshScaleView();
- }
- });
效果圖如下: