Mapa Android zoom v2 para mostrar todos os marcadores


286

Eu tenho 10 marcadores no GoogleMap. Quero ampliar o máximo possível e manter todos os marcadores à vista? Na versão anterior, isso pode ser alcançado, zoomToSpan()mas na v2 não tenho idéia de como fazer isso. Além disso, eu sei o raio do círculo que precisa ser visível.

Respostas:


810

Você deve usar a CameraUpdateclasse para realizar (provavelmente) todos os movimentos programáticos do mapa.

Para fazer isso, primeiro calcule os limites de todos os marcadores da seguinte maneira:

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

Em seguida, obtenha um objeto de descrição de movimento usando a fábrica CameraUpdateFactory:

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

Por fim, mova o mapa:

googleMap.moveCamera(cu);

Ou se você quiser uma animação:

googleMap.animateCamera(cu);

Isso é tudo :)

Esclarecimento 1

Quase todos os métodos de movimento exigem que o Mapobjeto tenha passado no processo de layout. Você pode esperar que isso aconteça usando a addOnGlobalLayoutListenerconstrução. Detalhes podem ser encontrados nos comentários a esta resposta e nas respostas restantes. Você também pode encontrar um código completo para definir a extensão do mapa usando addOnGlobalLayoutListeneraqui .

Esclarecimento 2

Um comentário observa que o uso desse método para apenas um marcador resulta no zoom do mapa definido para um nível de zoom "bizarro" (que eu acredito ser o nível máximo de zoom disponível para um determinado local). Eu acho que isso é esperado porque:

  1. A LatLngBounds boundsinstância terá northeastpropriedade igual a southwest, significando que a parte da área da terra coberta por isso boundsé exatamente zero. (Isso é lógico, pois um único marcador não tem área.)
  2. Ao passar boundspara CameraUpdateFactory.newLatLngBoundsvocê, solicite essencialmente um cálculo desse nível de zoom que bounds(com área zero) cubra toda a visualização do mapa.
  3. Você pode realmente executar esse cálculo em um pedaço de papel. O nível de zoom teórico que é a resposta é + ∞ (infinito positivo). Na prática, o Mapobjeto não suporta esse valor, portanto ele é fixado em um nível máximo mais razoável permitido para um determinado local.

Outra maneira de colocá-lo: como o Mapobjeto pode saber qual nível de zoom deve escolher para um único local ? Talvez o valor ideal deva ser 20 (se representar um endereço específico). Ou talvez 11 (se representa uma cidade). Ou talvez 6 (se representa um país). A API não é tão inteligente e a decisão é sua.

Portanto, você deve simplesmente verificar se markerspossui apenas um local e, se houver, usar um dos seguintes:

  • CameraUpdate cu = CameraUpdateFactory.newLatLng(marker.getPosition()) - vá para a posição do marcador, deixe o nível de zoom atual intacto.
  • CameraUpdate cu = CameraUpdateFactory.newLatLngZoom(marker.getPosition(), 12F) - vá para a posição do marcador, defina o nível de zoom para o valor escolhido arbitrariamente 12.

21
Deve-se observar que a movimentação não pode ocorrer a partir das chamadas onCreate, a exibição deve ser criada. Eu precisava usar addOnGlobalLayoutListener para obter a amostra apropriada.
Baruch Mesmo

6
@ Bar Bem, isso é parcialmente verdade. Para ser preciso: alguns métodos de movimento não funcionarão logo após a criação do objeto de mapa. A razão para isso é que o objeto do mapa ainda não foi medido, ou seja, não passou pelo processo de layout. Uma correção típica é usar addOnGlobalLayoutListener()ou post()com um apropriado Runnable. Esta é exatamente a razão pela qual não é possível obter as coordenadas da tela do marcador onCreate- consulte stackoverflow.com/q/14429877/1820695 No entanto, você pode usar alguns métodos antes mesmo que o layout ocorra - por exemplo. CameraUpdateFactory.newLatLngBounds()com 4 params .
andr

1
Cara, você salva meu dia ... na verdade, estava tentando fazer isso sozinho, calculava limites manualmente e fazia zoom enquanto o marcador estava ... estava funcionando muito feio, mas com o seu método simples, ele funciona como um encanto. Obrigado
Bibu

9
googleMap .setOnMapLoadedCallback (novo GoogleMap.OnMapLoadedCallback () {@Override public void onMapLoaded () {googleMap.moveCamera (cu);}}); Isso evitará o erro na medida.
Ramz 11/07

1
funciona de outra maneira, mas quando há um único marcador no mapa, ele aumenta o zoom para algum nível bizarro que o mapa fica desfocado. Como consertar isto?
precisa

124

Google Map V2

A solução a seguir funciona no Android Marshmallow 6 (API 23, API 24, API 25, API 26, API 27, API 28). Também funciona no 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);

7
Para um preenchimento mais bonito, use a altura em vez da largura e considere 20% em vez de 10%.
Noob

2
Às vezes, a resposta aceita causa um zoom estranho. Isso funciona como um encanto. Esta é uma resposta melhor.
Hüseyin Bülbül 20/03/19

1
Essa resposta funciona melhor que a aceita; a aceita causa algum comportamento distorcido.
Haytham

13

assim

Eu precisava usar addOnGlobalLayoutListener para obter a amostra apropriada

por exemplo, seu mapa do Google está dentro de 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
        }
    });

1
Veio aqui do tutorial da Udemy, ótimo trabalho, eles usaram sua resposta para resolver o problema.
Shantanu Shady

13

Não consegui usar o onGlobalLayoutlistener, então aqui está outra solução para evitar o "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."erro:

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

Este leva caminho mais longo do que o OnGlobalLayoutListener, então para mim ele ainda mostrou os mapas área default / zoom em primeiro lugar, antes de mover a câmera para minhas necessidades
Até

Como dar algum preenchimento, pois meus 2 marcadores são tocados com limites do mapa.
Pratik Butani 23/05/19

9

Trabalhando bem para mim.

A partir desse código, estou exibindo vários marcadores com zoom específico na tela do mapa.

// Variáveis ​​declaradas

private LatLngBounds bounds;
private LatLngBounds.Builder builder;

// Método para adicionar vários pontos de marcador com o ícone de desenho

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

    }

// Para adicionar vários marcadores visíveis no mapa

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

4

Nota - Esta não é uma solução para a pergunta original. Esta é uma solução para um dos subproblemas discutidos acima .

Solução para @andr Esclarecimento 2 -

É realmente problemático quando há apenas um marcador nos limites e, devido a isso, o nível de zoom é definido para um nível muito alto ( nível 21 ). E o Google não fornece nenhuma maneira de definir o nível máximo de zoom neste momento. Isso também pode acontecer quando houver mais de um marcador, mas todos eles são bem próximos um do outro. Também o mesmo problema ocorrerá.

Solução - Suponha que você queira que seu mapa nunca ultrapasse o nível de zoom 16. Depois de fazer -

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

Verifique se o nível de zoom ultrapassou o nível 16 (ou o que você quiser) -

float currentZoom = mMap.getCameraPosition().zoom;

E se esse nível for maior que 16, o que será apenas se houver muito menos marcadores ou se todos estiverem muito próximos um do outro, basta diminuir o zoom do mapa nessa posição específica, definindo o nível de zoom para 16.

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

Dessa forma, você nunca terá o problema do nível de zoom "bizarro" explicado muito bem por @andr também.


boa solução, mas se você colocar o objeto "cu" no onMapReady (), haverá um comportamento inadequado neste caso: local recebido -> o zoom é grande, então faça 16 -> então o usuário aproxima o zoom -> local recebido novamente e a câmera está volte ao nível 16 . Isso pode ser um problema quando o local é recebido em tempo real quase como ele vai manter a voltar para o nível 16.
Maher Nabil

"E o Google não fornece nenhuma maneira de definir o nível máximo de zoom neste momento." - Não é verdade: developers.google.com/android/reference/com/google/android/gms/…
user1209216

3

isso ajudaria .. do google apis demos

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

para obter informações claras, consulte este URL. https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/MarkerDemoActivity.java



1

Mostrar todos os marcadores com mapa do Google

Nestes métodos, armazene todos os marcadores e faça zoom automaticamente para mostrar todos os marcadores no mapa do 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);
}

0

Use o método "getCenterCoordinate" para obter a coordenada central e use em 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();
}

0

Eu tenho uma outra maneira de fazer essa mesma coisa funciona perfeitamente. então, a idéia por trás de mostrar todos os marcadores na tela, precisamos de um centro de comprimento e nível de zoom. Aqui está a função que fornecerá os dois e precisará de todos os objetos Latlng do marcador como entrada.

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

Esta função retorna o objeto Pair que você pode usar como

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

em vez de usar o preenchimento para manter os marcadores longe dos limites da tela, você pode ajustar o zoom em -1.


-3
   //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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.