Android 自定义高德地图比例尺

高德地图有提供自带的比例尺,但是不能改变位置,只能固定在屏幕右下角,做项目时,有些需求非要在某个位置,那就只能自定义了。

用高德自带的比例尺测试过,同一缩放等级,不同经纬度,比例尺也可能是不一样的,所以不能根据缩放等级处理。

好在高德地图有提供相应api

比例尺数据:AMap. getScalePerPixel()可以获取当前地图级别下1像素点对应的距离长度(米),然后可自定义比例尺长度(如100像素)。

效果图如下。

这里写图片描述

完整代码:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.amap.api.maps.AMap;
import com.autonavi.amap.mapcore.DPoint;
import com.autonavi.amap.mapcore.IPoint;
import com.autonavi.amap.mapcore.VirtualEarthProjection;

/**
 * @author gcz
 * blog https://blog.csdn.net/z19980115/article/details/78795329
 */
public final class MapScaleView extends View {
    private static final float a = 0.9F;

    //最大宽度,超过该宽度就向下一级转换
    private static final int maxWidth = 150;
    //比例尺文本
    private String text = "";
    //比例尺宽度
    private int lineWidth = 50;
    //视图高度
    private int viewHeight;
    //比例尺线条画笔
    private Paint linePaint;
    //比例尺文本画笔
    private Paint textPaint;
    //文本的区域
    private Rect rect;
    private IPoint point;
    //1dp转px后的值
    private float dp1 = 0.0F;
    private final int[] level = new int[]{10000000, 5000000, 2000000, 1000000, 500000, 200000, 100000, 50000, 30000, 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 25, 10, 5};

    public MapScaleView(Context context) {
        this(context, null);
    }

    public MapScaleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MapScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        this.rect = new Rect(0, 0, 0, 10);
        this.linePaint = new Paint();
        this.linePaint.setAntiAlias(true);
        this.linePaint.setColor(Color.WHITE);
        this.linePaint.setStrokeWidth(2.0F * a);
        this.linePaint.setStyle(Paint.Style.STROKE);
        
        this.textPaint = new Paint();
        this.textPaint.setAntiAlias(true);
        this.textPaint.setColor(Color.WHITE);
        this.textPaint.setTextSize(20.0F * a);
        this.dp1 = (float) getDp1(context);
        this.point = new IPoint();
    }

    public void destroy() {
        this.linePaint = null;
        this.textPaint = null;
        this.rect = null;
        this.text = null;
        this.point = null;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (this.text != null && !this.text.equals("") && this.lineWidth != 0) {
            this.textPaint.getTextBounds(this.text, 0, this.text.length(), this.rect);
            int x = 1;
            int y = viewHeight - this.rect.height() + 5;
            canvas.drawText(this.text, (this.lineWidth - this.rect.width()) / 2, y, this.textPaint);
            y += this.rect.height() - 5;
            //左竖线
            canvas.drawLine(x, y - 3.0F * this.dp1, x, y + a, this.linePaint);
            //横线
            canvas.drawLine(x, y - x, this.lineWidth, y - x, this.linePaint);
            //右竖线
            canvas.drawLine(this.lineWidth, y - 3.0F * this.dp1, this.lineWidth, y + a, this.linePaint);
        }
    }

    public void refreshScaleView(AMap aMap) {
        if (aMap == null) return;
        try {
            float zoom = aMap.getCameraPosition().zoom;

            //获取地图中心坐标
            aMap.getP20MapCenter(this.point);
            if (this.point != null) {
                DPoint dPoint = VirtualEarthProjection.PixelsToLatLong((long) this.point.x, (long) this.point.y, 20);
                float var3 = 1;
                //屏幕上1像素点对应的地图上距离长度,单位米,等同于aMap.getScalePerPixel();
                double scalePerPixel = (double) ((float) (Math.cos(dPoint.y * 3.141592653589793D / 180.0D) * 2.0D * 3.141592653589793D * 6378137.0D / (256.0D * Math.pow(2.0D, (double) zoom))));

                //比例尺宽度
                int lineWidth = (int) ((double) this.level[(int) zoom] / (scalePerPixel * (double) var3));
                String lineText = formatDistance(this.level[(int) zoom]);

                //如果超过最大宽度,则向下一级转换
                while (lineWidth > maxWidth && zoom < 19) {
                    zoom++;
                    lineWidth = (int) ((double) this.level[(int) zoom] / (scalePerPixel * (double) var3));
                    lineText = formatDistance(this.level[(int) zoom]);
                }
                this.lineWidth = lineWidth;
                this.text = lineText;
                
                dPoint.recycle();

                this.requestLayout();
                this.invalidate();
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int resultWidth;
        if (widthMode == MeasureSpec.EXACTLY) {//精确模式,写死宽度的情况
            resultWidth = widthSize;
        } else {
            resultWidth = Math.max(lineWidth, rect.width()) + 2;
            if (widthMode == MeasureSpec.AT_MOST) {//最大模式,父容器给的最大空间
                resultWidth = Math.min(resultWidth, widthSize);
            }
        }

        int heightSize = getHeightSize(heightMeasureSpec);
        viewHeight = heightSize;
        setMeasuredDimension(resultWidth, heightSize);
    }

    /**
     * 测量ScaleView的高度
     */
    private int getHeightSize(int heightMeasureSpec) {
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int height = 0;
        switch (mode) {
            case MeasureSpec.AT_MOST:
                height = (int) (rect.height() + 5 + 3F * this.dp1 + a);
                break;
            case MeasureSpec.EXACTLY: {
                height = MeasureSpec.getSize(heightMeasureSpec);
                break;
            }
            case MeasureSpec.UNSPECIFIED: {
                height = Math.max((int) (rect.height() + 5 + 3F * this.dp1 + a), MeasureSpec.getSize(heightMeasureSpec));
                break;
            }
        }

        return height;
    }

    public static String formatDistance(int m) {
        String distance;
        if (m < 1000) {
            distance = m + "m";
        } else {
            distance = m / 1000 + "km";
        }
        return distance;
    }

    /**
     * 1dp转px
     */
    public static int getDp1(Context var0) {
        float var1 = var0.getResources().getDisplayMetrics().density;
        return (int) (1.0F * var1 + 0.5F);
    }
}
使用方法: 和正常view一样添加到layout中,注意 修改包名
<com.xxx.xxx.MapScaleView
     android:id="@+id/blc"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content" />

OnCameraChangeListeneronCameraChange中调用比例尺的refreshScaleView()

override fun onCameraChange(p0: CameraPosition?) {
    if (p0 == null) return
	blc.refreshScaleView(mMap)
}

参考资料:
如何自定义指南针、缩放按钮、比例尺控件、定位按钮?
ArcGIS for Android 自定义地图比例尺

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值