当放大和缩小值应更改(比例尺)并在右下角显示缩放控件但我想在它上方替换时,如何在地图上添加比例尺。
如何使它成为可能?
谢谢
在 API 中没有设置比例尺的方法。在这种情况下,您应该自己实现它。这是如何做到这一点的简单示例。我已经改编了答案如何在 Android 上的 MapView 中添加地图比例?与谷歌地图一起工作。
class ScaleBar extends ImageView {
float mXOffset = 10;
float mYOffset = 10;
float mLineWidth = 3;
int mTextSize = 25;
boolean mIsImperial = false;
boolean mIsNautical = false;
boolean mIsLatitudeBar = true;
boolean mIsLongitudeBar = true;
private GoogleMap mMap;
float mXdpi;
float mYdpi;
public ScaleBar(Context context, GoogleMap map) {
super(context);
mMap = map;
mXdpi = context.getResources().getDisplayMetrics().xdpi;
mYdpi = context.getResources().getDisplayMetrics().ydpi;
}
@Override
public void onDraw(Canvas canvas) {
canvas.save();
drawScaleBarPicture(canvas);
canvas.restore();
}
private void drawScaleBarPicture(Canvas canvas) {
// We want the scale bar to be as long as the closest round-number miles/kilometers
// to 1-inch at the latitude at the current center of the screen.
Projection projection = mMap.getProjection();
if (projection == null) {
return;
}
final Paint barPaint = new Paint();
barPaint.setColor(Color.BLACK);
barPaint.setAntiAlias(true);
barPaint.setStrokeWidth(mLineWidth);
final Paint textPaint = new Paint();
textPaint.setColor(Color.BLACK);
textPaint.setAntiAlias(true);
textPaint.setTextSize(mTextSize);
drawXMetric(canvas, textPaint, barPaint);
drawYMetric(canvas, textPaint, barPaint);
}
private void drawXMetric(Canvas canvas, Paint textPaint, Paint barPaint) {
Projection projection = mMap.getProjection();
if (projection != null) {
LatLng p1 = projection.fromScreenLocation(new Point((int) ((getWidth() / 2) - (mXdpi / 2)), getHeight() / 2));
LatLng p2 = projection.fromScreenLocation(new Point((int) ((getWidth() / 2) + (mXdpi / 2)), getHeight() / 2));
Location locationP1 = new Location("ScaleBar location p1");
Location locationP2 = new Location("ScaleBar location p2");
locationP1.setLatitude(p1.latitude);
locationP2.setLatitude(p2.latitude);
locationP1.setLongitude(p1.longitude);
locationP2.setLongitude(p2.longitude);
float xMetersPerInch = locationP1.distanceTo(locationP2);
if (mIsLatitudeBar) {
String xMsg = scaleBarLengthText(xMetersPerInch, mIsImperial, mIsNautical);
Rect xTextRect = new Rect();
textPaint.getTextBounds(xMsg, 0, xMsg.length(), xTextRect);
int textSpacing = (int) (xTextRect.height() / 5.0);
canvas.drawRect(mXOffset, mYOffset, mXOffset + mXdpi, mYOffset + mLineWidth, barPaint);
canvas.drawRect(mXOffset + mXdpi, mYOffset, mXOffset + mXdpi + mLineWidth, mYOffset +
xTextRect.height() + mLineWidth + textSpacing, barPaint);
if (!mIsLongitudeBar) {
canvas.drawRect(mXOffset, mYOffset, mXOffset + mLineWidth, mYOffset +
xTextRect.height() + mLineWidth + textSpacing, barPaint);
}
canvas.drawText(xMsg, (mXOffset + mXdpi / 2 - xTextRect.width() / 2),
(mYOffset + xTextRect.height() + mLineWidth + textSpacing), textPaint);
}
}
}
private void drawYMetric(Canvas canvas, Paint textPaint, Paint barPaint) {
Projection projection = mMap.getProjection();
if (projection != null) {
Location locationP1 = new Location("ScaleBar location p1");
Location locationP2 = new Location("ScaleBar location p2");
LatLng p1 = projection.fromScreenLocation(new Point(getWidth() / 2,
(int) ((getHeight() / 2) - (mYdpi / 2))));
LatLng p2 = projection.fromScreenLocation(new Point(getWidth() / 2,
(int) ((getHeight() / 2) + (mYdpi / 2))));
locationP1.setLatitude(p1.latitude);
locationP2.setLatitude(p2.latitude);
locationP1.setLongitude(p1.longitude);
locationP2.setLongitude(p2.longitude);
float yMetersPerInch = locationP1.distanceTo(locationP2);
if (mIsLongitudeBar) {
String yMsg = scaleBarLengthText(yMetersPerInch, mIsImperial, mIsNautical);
Rect yTextRect = new Rect();
textPaint.getTextBounds(yMsg, 0, yMsg.length(), yTextRect);
int textSpacing = (int) (yTextRect.height() / 5.0);
canvas.drawRect(mXOffset, mYOffset, mXOffset + mLineWidth, mYOffset + mYdpi, barPaint);
canvas.drawRect(mXOffset, mYOffset + mYdpi, mXOffset + yTextRect.height() +
mLineWidth + textSpacing, mYOffset + mYdpi + mLineWidth, barPaint);
if (!mIsLatitudeBar) {
canvas.drawRect(mXOffset, mYOffset, mXOffset + yTextRect.height() +
mLineWidth + textSpacing, mYOffset + mLineWidth, barPaint);
}
float x = mXOffset + yTextRect.height() + mLineWidth + textSpacing;
float y = mYOffset + mYdpi / 2 + yTextRect.width() / 2;
canvas.rotate(-90, x, y);
canvas.drawText(yMsg, x, y + textSpacing, textPaint);
}
}
}
private String scaleBarLengthText(float meters, boolean imperial, boolean nautical) {
if (this.mIsImperial) {
if (meters >= 1609.344) {
return (meters / 1609.344) + "mi";
} else if (meters >= 1609.344/10) {
return ((meters / 160.9344) / 10.0) + "mi";
} else {
return (meters * 3.2808399) + "ft";
}
} else if (this.mIsNautical) {
if (meters >= 1852) {
return ((meters / 1852)) + "nm";
} else if (meters >= 1852/10) {
return (((meters / 185.2)) / 10.0) + "nm";
} else {
return ((meters * 3.2808399)) + "ft";
}
} else {
if (meters >= 1000) {
return ((meters / 1000)) + "km";
} else if (meters > 100) {
return ((meters / 100.0) / 10.0) + "km";
} else {
return meters + "m";
}
}
}
}
然后你可以通过这种方式将它添加到你的布局中:
RelativeLayout container = (RelativeLayout) findViewById(R.id.main_view);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(800, 800);
params.addRule(RelativeLayout.CENTER_HORIZONTAL);
params.addRule(RelativeLayout.CENTER_VERTICAL);
mScaleBar = new ScaleBar(this, mMap);
mScaleBar.setLayoutParams(params);
希望它对某人有用。
我根据以前的答案创建了小型库。
https://github.com/pengrad/MapScaleView
添加依赖
compile 'com.github.pengrad:mapscaleview:1.4.1'
包括在地图上的布局中
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/mapFragment"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.github.pengrad.mapscaleview.MapScaleView
android:id="@+id/scaleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="4dp"/>
</FrameLayout>
代码更新
MapScaleView scaleView = (MapScaleView) findViewById(R.id.scaleView);
CameraPosition cameraPosition = map.getCameraPosition();
// need to pass zoom and latitude
scaleView.update(cameraPosition.zoom, cameraPosition.target.latitude);
只需添加此代码..
googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
googleMap.getUiSettings().setZoomControlsEnabled(true);
googleMap.getUiSettings().setCompassEnabled(true);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
googleMap.getUiSettings().setAllGesturesEnabled(true);
googleMap.setMyLocationEnabled(true);
如前所述,Google Maps API 中没有提到比例尺。一个人需要自己编程。我从 Arkadiusz Cieśliński 那里得到了答案,并在此基础上进行了改进,以获得更像谷歌地图的比例视图。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.location.Location;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.Projection;
import com.google.android.gms.maps.model.LatLng;
import java.util.Locale;
import thermcam.georg.locationhistoryviewer.R;
import thermcam.georg.locationhistoryviewer.util.ResourcesUtil;
/**
* Created by georg on 18.04.17.
*/
public class MapScaleBar extends AppCompatImageView{
public enum ColorMode {
COLOR_MODE_AUTO,
COLOR_MODE_DARK,
COLOR_MODE_WHITE
}
private static final int FT_IN_MILE = 5280;
private static final int[] METERS = {1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000,
10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000};
private static final int[] FT = {1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000,
FT_IN_MILE, 2 * FT_IN_MILE, 5 * FT_IN_MILE, 10 * FT_IN_MILE, 20 * FT_IN_MILE, 50 * FT_IN_MILE,
100 * FT_IN_MILE, 200 * FT_IN_MILE, 500 * FT_IN_MILE, 1000 * FT_IN_MILE, 2000 * FT_IN_MILE};
float mXOffset = 10; // in px
float mYOffset = 10; // in px
private float mLineWidth = 4; // in dp
private float mTextSize = 15; // in dp
private float mTextPadding = 2; // in dp
public ColorMode mColorMode = ColorMode.COLOR_MODE_AUTO;
private GoogleMap mMap;
float mXdpi;
float mYdpi;
public MapScaleBar(Context context) {
this(context, null);
}
public MapScaleBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MapScaleBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mXdpi = context.getResources().getDisplayMetrics().xdpi;
mYdpi = context.getResources().getDisplayMetrics().ydpi;
setLineWidth(mLineWidth);
setTextSize(mTextSize);
setTextPadding(mTextPadding);
}
public void addTarget(GoogleMap map) {
this.mMap = map;
map.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
@Override
public void onCameraMove() {
invalidate();
}
});
}
public void setLineWidth(float lineWidth) {
this.mLineWidth = pxToDp(lineWidth);
}
public void setTextSize(float textSize) {
this.mTextSize = pxToDp(textSize);
}
public void setTextPadding(float textPadding) {
this.mTextPadding = pxToDp(textPadding);
}
public float pxToDp(float px) {
return px * (mYdpi / 160);
}
@Override
public void onDraw(Canvas canvas) {
if (mMap == null)
return;
canvas.save();
drawScaleBarPicture(canvas);
canvas.restore();
}
private void drawScaleBarPicture(Canvas canvas) {
// We want the scale bar to be as long as the closest round-number miles/kilometers
// to 1-inch at the latitude at the current center of the screen.
int darkC = ResourcesUtil.getColor(getResources(), R.color.textDarkPrimary, null);
int whiteC = ResourcesUtil.getColor(getResources(), R.color.textLightPrimary, null);
if (mColorMode == ColorMode.COLOR_MODE_WHITE ||
mColorMode == ColorMode.COLOR_MODE_AUTO && (mMap.getMapType() == GoogleMap.MAP_TYPE_SATELLITE || mMap.getMapType() == GoogleMap.MAP_TYPE_HYBRID))
drawXMetric(canvas, whiteC, darkC);
else
drawXMetric(canvas, darkC, whiteC);
}
private void drawXMetric(Canvas canvas, int fillColor, int outlineColor) {
Projection projection = mMap.getProjection();
LatLng p1 = projection.fromScreenLocation(new Point(
(int) ((getWidth() / 2) - (mXdpi / 2)), getHeight() / 2));
LatLng p2 = projection.fromScreenLocation(new Point((int) (
(getWidth() / 2) + (mXdpi / 2)), getHeight() / 2));
Location locationP1 = new Location("ScaleBar location p1");
Location locationP2 = new Location("ScaleBar location p2");
locationP1.setLatitude(p1.latitude);
locationP2.setLatitude(p2.latitude);
locationP1.setLongitude(p1.longitude);
locationP2.setLongitude(p2.longitude);
// PAINTS
final Paint barPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
barPaintFill.setStyle(Paint.Style.FILL);
barPaintFill.setColor(fillColor);
final Paint barPaintStroke = new Paint(barPaintFill);
barPaintStroke.setColor(outlineColor);
barPaintStroke.setStyle(Paint.Style.STROKE);
barPaintStroke.setStrokeWidth(mLineWidth/5);
final Paint textPaintFill = new Paint(barPaintFill);
textPaintFill.setTextSize(mTextSize);
final Paint textPaintStroke = new Paint(barPaintStroke);
textPaintStroke.setTextSize(mTextSize);
// LENGTH
float xMetersPerInch = locationP1.distanceTo(locationP2);
int nearestM = findNextSmallestInUnit(xMetersPerInch, false);
int nearestFT = findNextSmallestInUnit(xMetersPerInch, true);
float lengthM = mXdpi / xMetersPerInch * nearestM;
float lengthFT = mXdpi / xMetersPerInch * (nearestFT / 3.2808f);
String msgM = scaleBarLengthText(nearestM, false);
String msgFT = scaleBarLengthText(nearestFT, true);
float longest = Math.max(lengthFT, lengthM);
// Get text rects & cords
Rect textRectM = new Rect();
textPaintFill.getTextBounds(msgM, 0, msgM.length(), textRectM);
Rect textRectFT = new Rect();
textPaintFill.getTextBounds(msgFT, 0, msgFT.length(), textRectFT);
PointF textCoordM = new PointF(canvas.getWidth() - mXOffset - textRectM.width() - mTextPadding,
canvas.getHeight() - mYOffset - textRectFT.height() - mTextPadding * 2 - mLineWidth);
PointF textCoordFT = new PointF(canvas.getWidth() - mXOffset - textRectFT.width() - mTextPadding,
canvas.getHeight() - mYOffset - textRectFT.height() - textRectFT.top);
// PointF textCoordFT = new PointF()
// All movements are based in the bottom, right corner
Path barP = new Path();
barP.setFillType(Path.FillType.EVEN_ODD);
barP.moveTo(canvas.getWidth() - mXOffset, canvas.getHeight() - mYOffset - textRectFT.height() - mTextPadding);
barP.rLineTo(- lengthFT + mLineWidth, 0);
barP.rLineTo(0, textRectFT.height() * 0.6f);
barP.rLineTo(- mLineWidth, 0);
barP.rLineTo(0, -textRectFT.height() * 0.6f);
barP.rLineTo(lengthFT - longest, 0);
barP.rLineTo(0, - mLineWidth);
barP.rLineTo(longest - lengthM, 0);
barP.rLineTo(0, -textRectM.height() * 0.6f);
barP.rLineTo(mLineWidth, 0);
barP.rLineTo(0, textRectM.height() * 0.6f);
barP.rLineTo(lengthM - mLineWidth, 0);
barP.close();
canvas.drawPath(barP, barPaintFill);
canvas.drawPath(barP, barPaintStroke);
canvas.drawText(msgM, textCoordM.x, textCoordM.y, textPaintStroke);
canvas.drawText(msgM, textCoordM.x, textCoordM.y, textPaintFill);
canvas.drawText(msgFT, textCoordFT.x, textCoordFT.y, textPaintStroke);
canvas.drawText(msgFT, textCoordFT.x, textCoordFT.y, textPaintFill);
}
private int findNextSmallestInUnit(float meters, boolean imperial) {
int[] data = imperial ? FT : METERS;
float value = meters * (imperial ? 3.2808f : 1);
int closest = data[0];
for (int elem: data) {
if (elem - value > 0)
break;
else
closest = elem;
}
return closest;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension((int)mXdpi, (int)(mTextPadding * 4 + mTextSize * 2 + mLineWidth));
}
private String scaleBarLengthText(int value, boolean imperial) {
if (imperial) {
if (value >= FT_IN_MILE)
return String.format(Locale.getDefault(), "%d mi", value / FT_IN_MILE);
else
return String.format(Locale.getDefault(), "%d ft", value);
} else {
if (value >= 1000)
return String.format(Locale.getDefault(), "%d km", value / 1000);
else
return String.format(Locale.getDefault(), "%d m", value);
}
}
}
并且可以通过将其添加到您的layout.xml
. 例如像这样:
<package.id.MapScaleBar
android:id="@+id/scaleBar"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
然后你只需要在addTarget
它的方法中添加一个谷歌地图实例,例如在onMapReady(GoogleMap googleMap)
函数中。
MapScaleBar scaleBar = (MapScaleBar)findViewById(R.id.scaleBar);
scaleBar.mColorMode = MapScaleBar.ColorMode.COLOR_MODE_DARK; // normal
scaleBar.mColorMode = MapScaleBar.ColorMode.COLOR_MODE_WHITE; // inverted
scaleBar.mColorMode = MapScaleBar.ColorMode.COLOR_MODE_AUTO; // detect automatically after Google Map style
scaleBar.addTarget(mMap);
这是我的代码(使用 pengrad 的最新版本)。
将此添加到 gradle 文件中。
dependencies {
implementation 'com.github.pengrad:mapscaleview:1.4.2'
}
然后,您必须在布局文件中使用地图添加比例组件。
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/mapFragment"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.github.pengrad.mapscaleview.MapScaleView
android:id="@+id/scaleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="4dp"/>
</FrameLayout>
最后一步是链接活动或片段中的两个组件(请注意,现在不推荐使用 OnCameraChangeListener)。
public class MyFragment extends Fragment implements OnMapReadyCallback, GoogleMap.OnCameraIdleListener, GoogleMap.OnCameraMoveListener, GoogleMap.OnCameraChangeListener {
private GoogleMap googleMap;
private MapScaleView mapScaleView;
....
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
viewModel =
ViewModelProviders.of(getActivity()).get(ViewModel.class);
View root = inflater.inflate(R.layout.fragment_layout, container, false);
// Get the SupportMapFragment and request notification
// when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.home_map);
mapFragment.getMapAsync(this);
// scale view for map
mapScaleView = (MapScaleView) root.findViewById(R.id.home_map_scale_view);
}
....
@Override
public void onMapReady(GoogleMap googleMap) {
this.googleMap = googleMap;
googleMap.setOnCameraMoveListener(this);
googleMap.setOnCameraIdleListener(this);
googleMap.setOnCameraChangeListener(this);
}
...
@Override
public void onCameraMove() {
CameraPosition cameraPosition = googleMap.getCameraPosition();
if(mapScaleView != null)
mapScaleView.update(cameraPosition.zoom, cameraPosition.target.latitude);
}
@Override
public void onCameraIdle() {
CameraPosition cameraPosition = googleMap.getCameraPosition();
if(mapScaleView != null)
mapScaleView.update(cameraPosition.zoom, cameraPosition.target.latitude);
}
@Override
public void onCameraChange(CameraPosition cameraPosition) {
if(mapScaleView != null)
mapScaleView.update(cameraPosition.zoom, cameraPosition.target.latitude);
}
}
然后您可以自定义 viewScale 例如:
mapScaleView.metersAndMiles() // default
mapScaleView.metersOnly()
mapScaleView.milesOnly()
希望它会帮助你。