Android 地图 v2 缩放以显示所有标记

Posted

技术标签:

【中文标题】Android 地图 v2 缩放以显示所有标记【英文标题】:Android map v2 zoom to show all the markers 【发布时间】:2013-01-27 12:05:15 【问题描述】:

GoogleMap 中有 10 个标记。我想尽可能放大并保留所有标记?在早期版本中,这可以通过zoomToSpan() 实现,但在 v2 中我不知道该怎么做。此外,我知道需要可见的圆的半径。

【问题讨论】:

【参考方案1】:

您应该使用CameraUpdate 类来执行(可能)所有程序化地图移动。

为此,首先计算所有标记的边界,如下所示:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (Marker marker : markers) 
    builder.include(marker.getPosition());

LatLngBounds bounds = builder.build();

然后通过工厂获取一个动作描述对象:CameraUpdateFactory

int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);

最后移动地图:

googleMap.moveCamera(cu);

或者如果你想要一个动画:

googleMap.animateCamera(cu);

就是这样:)

说明 1

几乎所有的移动方法都需要Map 对象通过布局过程。您可以使用addOnGlobalLayoutListener 构造等待这种情况发生。可以在 cmets 中找到此答案和其余答案的详细信息。您也可以找到complete code for setting map extent using addOnGlobalLayoutListener here。

说明 2

一条评论指出,仅对一个标记使用此方法会导致地图缩放设置为“奇怪”的缩放级别(我认为这是给定位置可用的最大缩放级别)。我认为这是意料之中的,因为:

    LatLngBounds bounds 实例将具有等于southwestnortheast 属性,这意味着该bounds 覆盖的地球区域部分正好为零。 (这是合乎逻辑的,因为单个标记没有区域。) 通过将bounds 传递给CameraUpdateFactory.newLatLngBounds,您实际上要求计算bounds(具有零区域)将覆盖整个地图视图的缩放级别。 您实际上可以在一张纸上执行此计算。作为答案的理论缩放级别是+∞(正无穷大)。实际上,Map 对象不支持此值,因此它被限制在给定位置允许的更合理的最大值。

另一种说法:Map 对象如何知道它应该为单个位置选择什么缩放级别?也许最佳值应该是 20(如果它代表一个特定的地址)。或者可能是 11(如果它代表一个城镇)。或者可能是 6(如果它代表一个国家)。 API 不是那么聪明,决定权在您手中。

因此,您应该简单地检查markers 是否只有一个位置,如果是,请使用以下之一:

CameraUpdate cu = CameraUpdateFactory.newLatLng(marker.getPosition()) - 转到标记位置,保持当前缩放级别不变。 CameraUpdate cu = CameraUpdateFactory.newLatLngZoom(marker.getPosition(), 12F) - 转到标记位置,将缩放级别设置为任意选择的值 12。

【讨论】:

应该注意,移动不能从 onCreate 调用发生,必须创建视图。我需要使用 addOnGlobalLayoutListener 来获取适当的示例。 @Bar 好吧,这部分是正确的。准确地说:一些 移动方法在创建地图对象后将无法正常工作。其原因是地图对象尚未测量,即尚未经过布局过程。一个典型的解决方法是使用addOnGlobalLayoutListener()post() 和适当的Runnable。这正是无法在onCreate 中获取标记屏幕坐标的原因 - 请参阅***.com/q/14429877/1820695 但是您可以在布局发生之前使用一些方法 - 例如。 CameraUpdateFactory.newLatLngBounds() 带有 4 个参数 伙计,你拯救了我的一天......实际上是在尝试自己做,手动计算边界,并在标记位于其中时进行缩放......工作非常难看,但你的简单方法,它就像一个魅力。谢谢 googleMap .setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() @Override public void onMapLoaded() googleMap.moveCamera(cu); );这将避免测量误差。 它可以正常工作,但是当地图上只有一个标记时,它会缩放到某个奇怪的级别,从而使地图变得模糊。如何解决这个问题?【参考方案2】:

谷歌地图 V2

以下解决方案适用于 android Marshmallow 6(API 23、API 24、API 25、API 26、API 27、API 28)。它也适用于 Xamarin。

LatLngBounds.Builder builder = new LatLngBounds.Builder();

//the include method will calculate the min and max bound.
builder.include(marker1.getPosition());
builder.include(marker2.getPosition());
builder.include(marker3.getPosition());
builder.include(marker4.getPosition());

LatLngBounds bounds = builder.build();

int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
int padding = (int) (width * 0.10); // offset from edges of the map 10% of screen

CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding);

mMap.animateCamera(cu);

【讨论】:

为了更好看的填充,使用高度而不是宽度,并取 20% 而不是 10%。 接受的答案有时会导致奇怪的缩放。这就像一个魅力。这是一个更好的答案。 这个答案比接受的更好,接受的会导致一些奇怪的行为。【参考方案3】:

我无法使用 onGlobalLayoutlistener,所以这里有另一个解决方案来防止 "Map size can't be 0. Most likely, layout has not yet occured for the map view. Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions."错误:

mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback()  
@Override 
public void onMapLoaded()  
    mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 15));
  
);

【讨论】:

这个比 OnGlobalLayoutListener 花费的时间更长,所以对我来说,它仍然首先显示地图默认区域/缩放,然后再根据需要移动相机 如何在我的 2 个标记与地图边界接触时提供一些填充。【参考方案4】:

所以

我需要使用 addOnGlobalLayoutListener 来获取合适的样本

例如,您的 Google 地图位于 RelativeLayout:

RelativeLayout mapLayout = (RelativeLayout)findViewById(R.id.map_layout);
mapLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() 
        @Override
        public void onGlobalLayout() 
            //and write code, which you can see in answer above
        
    );

【讨论】:

从 Udemy 教程来到这里,伟大的工作人员,他们用你的答案解决了问题。【参考方案5】:

对我来说工作得很好。

从这段代码中,我在地图屏幕上显示了多个具有特定缩放比例的标记。

//声明的变量

private LatLngBounds bounds;
private LatLngBounds.Builder builder;

// 用drawable icon添加多个标记点的方法

private void drawMarker(LatLng point, String text) 

        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(point).title(text).icon(BitmapDescriptorFactory.fromResource(R.drawable.icon));
        mMap.addMarker(markerOptions);
        builder.include(markerOptions.getPosition());

    

// 用于添加地图上可见的多个标记

@Override
    public void onMapReady(GoogleMap googleMap) 
        mMap = googleMap;
        builder = new LatLngBounds.Builder();
    for (int i = 0; i < locationList.size(); i++) 

        drawMarker(new LatLng(Double.parseDouble(locationList.get(i).getLatitude()), Double.parseDouble(locationList.get(i).getLongitude())), locationList.get(i).getNo());

     
     bounds = builder.build();
     CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
     mMap.animateCamera(cu);

【讨论】:

【参考方案6】:

注意 - 这不是原始问题的解决方案。这是above 讨论的子问题之一的解决方案。

@andr 的解决方案澄清 2 -

当边界中只有一个标记并且缩放级别设置为非常高的级别(级别 21)时,这确实有问题。谷歌目前没有提供任何设置最大缩放级别的方法。当有超过 1 个标记但它们都彼此非常接近时,也会发生这种情况。那么也会出现同样的问题。

解决方案 - 假设您希望地图的缩放级别永远不会超过 16。然后在做之后 -

CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);
mMap.moveCamera(cu);

检查您的缩放级别是否超过了 16 级(或任何您想要的) -

float currentZoom = mMap.getCameraPosition().zoom;

如果此级别大于 16,仅当标记非常少或所有标记彼此非常接近时才会出现这种情况,那么只需通过设置缩放级别来缩小该特定位置的地图到 16 点。

mMap.moveCamera(CameraUpdateFactory.zoomTo(16));

这样你就永远不会遇到@andr 很好地解释的“奇怪”缩放级别的问题。

【讨论】:

很好的解决方案,但如果你将“cu”对象放在 onMapReady() 中,那么在这种情况下会出现错误行为:接收到的位置 -> 缩放很大,所以将其设为 16 -> 然后用户放大 -> 再次收到位置并将相机放回 16 级。当几乎实时接收到位置时,这可能是一个问题,因为它会不断返回到 16 级。 "Google 目前没有提供任何设置最大缩放级别的方法。" - 不正确:developers.google.com/android/reference/com/google/android/gms/…【参考方案7】:

这会有所帮助..来自 google apis 演示

private List<Marker> markerList = new ArrayList<>();
Marker marker = mGoogleMap.addMarker(new MarkerOptions().position(geoLatLng)
                .title(title));
markerList.add(marker);
    // Pan to see all markers in view.
    // Cannot zoom to bounds until the map has a size.
    final View mapView = getSupportFragmentManager().findFragmentById(R.id.map).getView();
    if (mapView!=null) 
        if (mapView.getViewTreeObserver().isAlive()) 
            mapView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() 
                @SuppressWarnings("deprecation") // We use the new method when supported
                @SuppressLint("NewApi") // We check which build version we are using.
                @Override
                public void onGlobalLayout() 
                    //Calculate the markers to get their position
                    LatLngBounds.Builder b = new LatLngBounds.Builder();
                    for (Marker m : markerList) 
                        b.include(m.getPosition());
                    
                    // also include current location to include in the view
                    b.include(new LatLng(mLocation.getLatitude(),mLocation.getLongitude()));

                    LatLngBounds bounds = b.build();
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) 
                        mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                     else 
                        mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    
                    mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
                
            );
        
    

要了解清楚的信息,请查看此网址。 https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/MarkerDemoActivity.java

【讨论】:

谢谢,它甚至在循环中也能正常工作。【参考方案8】:

使用 Google 地图显示所有标记

在这些方法中存储所有标记并自动缩放以显示谷歌地图中的所有标记。

// Declare the Markers List.
List<MarkerOptions> markerList;
private BitmapDescriptor vnrPoint,banPoint;


public void storeAllMarkers()

      markerList=new ArrayList<>();
      markerList.removeAll(markerList);


      // latitude and longitude of Virudhunagar

      double latitude1=9.587209;
      double longitude1=77.951431;
   vnrPoint=BitmapDescriptorFactory.fromResource(R.drawable.location_icon_1);
      LatLng vnr = new LatLng(latitude1, longitude1);
      MarkerOptions vnrMarker = new MarkerOptions();
      vnrMarker.position(vnr);
      vnrMarker.icon(vnrPoint);
      markerList.add(vnrMarker);

      // latitude and longitude of Bengaluru

      double latitude2=12.972442;
      double longitude2=77.580643;

    banPoint=BitmapDescriptorFactory.fromResource(R.drawable.location_icon_2);

      LatLng ban = new LatLng(latitude2, longitude2);
      MarkerOptions bengalureMarker = new MarkerOptions();
      bengalureMarker.position(ban);
      bengalureMarker.icon(banPoint);
      markerList.add(bengalureMarker);

      // You can add any numbers of MarkerOptions like this.

     showAllMarkers();

 


public void showAllMarkers()

    LatLngBounds.Builder builder = new LatLngBounds.Builder();

    for (MarkerOptions m : markerList) 
        builder.include(m.getPosition());
    

    LatLngBounds bounds = builder.build();

    int width = getResources().getDisplayMetrics().widthPixels;
    int height = getResources().getDisplayMetrics().heightPixels;
    int padding = (int) (width * 0.30); 

    // Zoom and animate the google map to show all markers

    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding);
    googleMap.animateCamera(cu);

【讨论】:

【参考方案9】:

我有类似的问题,使用以下代码解决了这个问题:

CameraUpdateFactory.newLatLngBounds(bounds, 200, 200, 5)在我的情况下通常位置差异不超过两个相邻城市。

zoom to fit all markers on map google maps v2

【讨论】:

【参考方案10】:

我还有另一种方法可以完美地完成同样的事情。所以在屏幕上显示所有标记背后的想法是我们需要一个中心纬度和缩放级别。这是一个函数,它将为您提供并需要所有标记的 Latlng 对象作为输入。

 public Pair<LatLng, Integer> getCenterWithZoomLevel(LatLng... l) 
    float max = 0;

    if (l == null || l.length == 0) 
        return null;
    
    LatLngBounds.Builder b = new LatLngBounds.Builder();
    for (int count = 0; count < l.length; count++) 
        if (l[count] == null) 
            continue;
        
        b.include(l[count]);
    

    LatLng center = b.build().getCenter();

    float distance = 0;
    for (int count = 0; count < l.length; count++) 
        if (l[count] == null) 
            continue;
        
        distance = distance(center, l[count]);
        if (distance > max) 
            max = distance;
        
    

    double scale = max / 1000;
    int zoom = ((int) (16 - Math.log(scale) / Math.log(2)));
    return new Pair<LatLng, Integer>(center, zoom);

此函数返回 Pair 对象,您可以像这样使用它

对对 = getCenterWithZoomLevel(l1,l2,l3..); mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(pair.first, pair.second));

您可以将缩放调整为 -1,而不是使用填充来使标记远离屏幕边界。

【讨论】:

【参考方案11】:

使用“getCenterCoordinate”方法获取中心坐标并在CameraPosition中使用。

private void setUpMap() 
    mMap.setMyLocationEnabled(true);
    mMap.getUiSettings().setScrollGesturesEnabled(true);
    mMap.getUiSettings().setTiltGesturesEnabled(true);
    mMap.getUiSettings().setRotateGesturesEnabled(true);

    clientMarker = mMap.addMarker(new MarkerOptions()
            .position(new LatLng(Double.valueOf(-12.1024174), Double.valueOf(-77.0262274)))
            .icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_taxi))
    );
    clientMarker = mMap.addMarker(new MarkerOptions()
            .position(new LatLng(Double.valueOf(-12.1024637), Double.valueOf(-77.0242617)))
            .icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_location))
    );

    camPos = new CameraPosition.Builder()
            .target(getCenterCoordinate())
            .zoom(17)
            .build();
    camUpd3 = CameraUpdateFactory.newCameraPosition(camPos);
    mMap.animateCamera(camUpd3);



public LatLng getCenterCoordinate()
    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    builder.include(new LatLng(Double.valueOf(-12.1024174), Double.valueOf(-77.0262274)));
    builder.include(new LatLng(Double.valueOf(-12.1024637), Double.valueOf(-77.0242617)));
    LatLngBounds bounds = builder.build();
    return bounds.getCenter();

【讨论】:

【参考方案12】:

我在 Kotlin 中使用片段显示多个标记时遇到了同样的问题

首先声明一个标记列表

private lateinit var markers: MutableList<Marker>

在frament的oncreate方法中初始化这个

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
                         ): View? 
    //initialize markers list

    markers = mutableListOf()
   
    return inflater.inflate(R.layout.fragment_driver_map, container, false)

在 OnMapReadyCallback 上将标记添加到标记列表中

private val callback = OnMapReadyCallback  googleMap ->

    map = googleMap

    markers.add(
        map.addMarker(
            MarkerOptions().position(riderLatLng)
                .title("Driver")
                .snippet("Driver")
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))))


    markers.add(
        map.addMarker(
            MarkerOptions().position(driverLatLng)
                .title("Driver")
                .snippet("Driver")
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))))

仍在回调中

//create builder
    val builder = LatLngBounds.builder()

    //loop through the markers list
    for (marker in markers) 

        builder.include(marker.position)
    
    //create a bound
    val bounds = builder.build()

    //set a 200 pixels padding from the edge of the screen
    val cu = CameraUpdateFactory.newLatLngBounds(bounds,200)
    
    //move and animate the camera
    map.moveCamera(cu)
    //animate camera by providing zoom and duration args, callBack set to null
    map.animateCamera(CameraUpdateFactory.zoomTo(10f), 2000, null)

编码们快乐

【讨论】:

【参考方案13】:
   //For adding a marker in Google map
        MarkerOptions mp = new MarkerOptions();
        mp.position(new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude)));
        mp.snippet(strAddress);
        map.addMarker(mp);

        try 

            b = new LatLngBounds.Builder();

            if (MapDetailsList.list != null && MapDetailsList.list.size() > 0) 

                for (int i = 0; i < MapDetailsList.list.size(); i++) 

                    b.include(new LatLng(Double.parseDouble(MapDetailsList.list.get(i).getLatitude()),
                            Double.parseDouble(MapDetailsList.list.get(i).getLongitude())));

                
                LatLngBounds bounds = b.build();

                DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
                int width = displayMetrics.widthPixels;
                int height = displayMetrics.heightPixels;

                // Change the padding as per needed
                CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width-200, height-200, 5);
                // map.setCenter(bounds.getCenter());

                map.animateCamera(cu);

            

         catch (Exception e) 

        

http://i64.tinypic.com/2qjybh4.png

http://i63.tinypic.com/flzwus.png

http://i63.tinypic.com/112g5fm.png

【讨论】:

以上是关于Android 地图 v2 缩放以显示所有标记的主要内容,如果未能解决你的问题,请参考以下文章

如何设置谷歌地图缩放级别以显示所有标记?

设置地图缩放以覆盖所有标记;确保以特定标记为中心

缩放以适合 LeafletJS 地图上的所有标记

新手中心/设置地图缩放以覆盖所有可见标记? [复制]

Google Maps v2 设置缩放级别

Android 谷歌地图缩放以适​​合标记和当前位置