高德地图有提供自带的比例尺,但是不能改变位置,只能固定在屏幕右下角,做项目时,有些需求非要在某个位置,那就只能自定义了。
用高德自带的比例尺测试过,同一缩放等级,不同经纬度,比例尺也可能是不一样的,所以不能根据缩放等级处理。
好在高德地图有提供相应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.youkuaiyun.com/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" />
在OnCameraChangeListener
的onCameraChange
中调用比例尺的refreshScaleView()
override fun onCameraChange(p0: CameraPosition?) {
if (p0 == null) return
blc.refreshScaleView(mMap)
}