【问题标题】:How to show the scale bar and zoom controls on the map?如何在地图上显示比例尺和缩放控件?
【发布时间】:2013-07-25 00:52:30
【问题描述】:

当放大和缩小值应该改变时如何在地图上添加比例尺(比例尺)并在右下角显示缩放控件但我想在它上方替换。

如何实现?

谢谢

【问题讨论】:

    标签: android zooming elevation


    【解决方案1】:

    只需添加此代码..

        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);
    

    【讨论】:

      【解决方案2】:

      在 API 中没有设置比例尺的方法。在这种情况下,您应该自己实现它。这是如何做到这一点的简单示例。我已将答案 How to add a map scale in MapView on Android? 中的代码改编为与 GoogleMap 一起使用。

      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);
      

      希望它对某人有用。

      【讨论】:

      • 效果很好。只需添加一些 onTouch 处理程序,然后添加 mScaleBar.invalidate()
      • @dariusiv 在类构造函数中,您可以添加下一个代码使视图无效:mMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { @Override public void onCameraMove() { invalidate(); } });
      【解决方案3】:

      如前所述,Google Maps API 中没有提到比例尺。一个人需要自己编程。我从 Arkadiusz Cieśliński 那里得到了答案,并对此进行了改进,以获得更像 Google 地图的比例视图。

      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 方法中添加一个Google Map 实例,例如在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);
      

      【讨论】:

        【解决方案4】:

        我根据以前的答案创建了小型库。
        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);
        

        【讨论】:

        • 谢谢!对于其他用户,请使用最新的可用版本。对于 1.2.0 版本,比例是错误的。
        【解决方案5】:

        这是我的代码(使用最新版本的 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 eg:

        mapScaleView.metersAndMiles() // default
        mapScaleView.metersOnly()
        mapScaleView.milesOnly()
        

        希望对你有所帮助。

        【讨论】:

          猜你喜欢
          • 2013-07-16
          • 1970-01-01
          • 1970-01-01
          • 2020-08-31
          • 1970-01-01
          • 2012-06-25
          • 2019-08-28
          • 2012-03-14
          • 1970-01-01
          相关资源
          最近更新 更多