moveCamera mit CameraUpdateFactory.newLatLngBounds stürzt ab


96

Ich benutze die neue Android Google Maps API .

Ich erstelle eine Aktivität, die ein MapFragment enthält. In der Aktivität onResumesetze ich die Markierungen in das GoogleMap-Objekt und definiere dann einen Begrenzungsrahmen für die Karte, der alle Markierungen enthält.

Dies verwendet den folgenden Pseudocode:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
while(data) {
   LatLng latlng = getPosition();
   builder.include(latlng);
}
CameraUpdate cameraUpdate = CameraUpdateFactory
   .newLatLngBounds(builder.build(), 10);
map.moveCamera(cameraUpdate);

Der Aufruf von führt dazu, map.moveCamera()dass meine Anwendung mit dem folgenden Stapel abstürzt:

Caused by: java.lang.IllegalStateException: 
    Map size should not be 0. Most likely, layout has not yet 

    at maps.am.r.b(Unknown Source)
    at maps.y.q.a(Unknown Source)
    at maps.y.au.a(Unknown Source)
    at maps.y.ae.moveCamera(Unknown Source)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$Stub
        .onTransact(IGoogleMapDelegate.java:83)
    at android.os.Binder.transact(Binder.java:310)
    at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a
        .moveCamera(Unknown Source)
    at com.google.android.gms.maps.GoogleMap.moveCamera(Unknown Source)
    at ShowMapActivity.drawMapMarkers(ShowMapActivity.java:91)
    at ShowMapActivity.onResume(ShowMapActivity.java:58)
    at android.app.Instrumentation
        .callActivityOnResume(Instrumentation.java:1185)
    at android.app.Activity.performResume(Activity.java:5182)
    at android.app.ActivityThread
        .performResumeActivity(ActivityThread.java:2732)

Wenn - anstelle der newLatLngBounds()Factory-Methode die Methode verwendet newLatLngZoom()wird, tritt nicht dieselbe Falle auf.

Ist der onResumebeste Ort, um die Markierungen auf das GoogleMap-Objekt zu zeichnen, oder sollte ich die Markierungen zeichnen und die Kameraposition woanders einstellen?

Antworten:


133

Sie können die einfache newLatLngBounds- Methode in OnCameraChangeListener verwenden . Alles wird perfekt funktionieren und Sie müssen die Bildschirmgröße nicht berechnen. Dieses Ereignis tritt nach der Berechnung der Kartengröße auf (soweit ich weiß).

Beispiel:

map.setOnCameraChangeListener(new OnCameraChangeListener() {

    @Override
    public void onCameraChange(CameraPosition arg0) {
        // Move camera.
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));
        // Remove listener to prevent position reset on camera move.
        map.setOnCameraChangeListener(null);
    }
});

1
Dies funktioniert, weil setOnCameraChangeListener garantiert mindestens einmal ausgeführt wird, nachdem die Karte einem Layout unterzogen wurde. Ist das zukunftssicher?
Glenn Bech

3
@SteelRat Deshalb wäre ich gegen die Verwendung der Lösung. Sie wissen nie, wann Google dies ändert oder ob es auf allen Android-Versionen oder -Geräten so funktioniert.
Glenn Bech

1
@SteelRat Ich stimme zu, aber es kann mehr als einmal im Leben der Aktivität aufgerufen werden? Ich mache nur das Argument, dass es keine sehr elegante Lösung ist, auch wenn es in diesem Fall funktioniert. Ich denke, der OnGlobalLayoutListener / Treelistener ist ein korrekterer Weg, dies zu tun.
Glenn Bech

4
möglicherweise map.moveCamera hinzufügen (CameraUpdateFactory.scrollBy (1, 1)); nach dem obigen Code wird der Trick machen?
MEIN

3
setOnCameraChangeListenerist jetzt veraltet
osrl

56

Diese Antworten sind in Ordnung, aber ich würde einen anderen Ansatz wählen, einen einfacheren. Wenn die Methode erst funktioniert, nachdem die Karte angelegt wurde, warten Sie einfach darauf:

map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
    @Override
    public void onMapLoaded() {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
    }
});

1
Nach langem Experimentieren stellte ich fest, dass ich eine Kombination aus dieser und der obigen Antwort von Daniele implementieren musste . Manchmal war die Karte noch nicht geladen, manchmal war das Layout noch nicht fertig. Ich konnte die Kamera nicht wie gewünscht bewegen, bis BEIDE davon passierten.
RobP

2
Warnung für diese Methode aus den Dokumenten: "Dieses Ereignis wird nicht ausgelöst, wenn die Karte aufgrund von Verbindungsproblemen nie geladen wird oder wenn sich die Karte ständig ändert und das Laden nie abgeschlossen wird, weil der Benutzer ständig mit der Karte interagiert ." (Hervorhebung von mir).
stkent

1
Dies verzögert sich länger als nötig, da darauf gewartet wird, dass die Kartenkacheln an der falschen Position geladen werden, bevor sie an die richtige Position verschoben werden.
John Patterson

1
Meistens dauert es zwischen 3 und 10 Sekunden, um zu feuern, was nicht akzeptabel ist.
Nima G

Hier können Sie die Kamera bewegen. Ich würde vorschlagen, map.animateCamera anstelle von map.moveCamera
Bharat Dodeja

51

OK, ich habe das herausgefunden. Wie hier dokumentiert , kann die API nicht vor dem Layout verwendet werden.

Die richtige API wird wie folgt beschrieben:

Hinweis: Verwenden Sie die einfachere Methode newLatLngBounds (Grenze, Auffüllung) nur, um ein CameraUpdate zu generieren, wenn es zum Verschieben der Kamera verwendet werden soll, nachdem die Karte einem Layout unterzogen wurde. Während des Layouts berechnet die API die Anzeigegrenzen der Karte, die zum korrekten Projizieren des Begrenzungsrahmens erforderlich sind. Im Vergleich dazu können Sie das von der komplexeren Methode newLatLngBounds (Grenze, Breite, Höhe, Auffüllung) zurückgegebene CameraUpdate jederzeit verwenden, noch bevor die Karte einem Layout unterzogen wurde, da die API die Anzeigegrenzen aus den von Ihnen übergebenen Argumenten berechnet.

Um das Problem zu beheben, habe ich meine Bildschirmgröße berechnet und die Breite und Höhe angegeben

public static CameraUpdate newLatLngBounds(
    LatLngBounds bounds, int width, int height, int padding)

Dadurch konnte ich das Begrenzungsrahmen-Vorlayout festlegen.


18
Was aber, wenn die Karte nicht den gesamten Bildschirm einnimmt?
Theblang

In meinem Fall hatte ich gerade einen größeren Bildschirm als tatsächlich angenommen angenommen und musste die Polsterung genauer berechnen, damit sie tatsächlich nicht zu groß war. Ein viel einfacherer Grund.
Rfay

2
Können Sie zeigen, wie man es basierend auf der Bildschirmgröße berechnet?
Daniel Gomez Rico

Dies stürzt nicht ab, aber die zu verwendende Breite und Höhe liegen nahe an den Vermutungen. Der genaue zu zeichnende Bereich ist wirklich unvorhersehbar.
Vince

34

Die Lösung ist einfacher als das ....

// Pan to see all markers in view.
try {
    this.gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
} catch (IllegalStateException e) {
    // layout not yet initialized
    final View mapView = getFragmentManager()
       .findFragmentById(R.id.map).getView();
    if (mapView.getViewTreeObserver().isAlive()) {
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @SuppressLint("NewApi")
            // We check which build version we are using.
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                    mapView.getViewTreeObserver()
                        .removeGlobalOnLayoutListener(this);
                } else {
                    mapView.getViewTreeObserver()
                        .removeOnGlobalLayoutListener(this);
                }
                gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
            }
        });
    }
}

Fangen Sie IllegalStateExceptionstattdessen den globalen Listener für die Ansicht ab und verwenden Sie ihn. Wenn Sie die gebundene Größe im Voraus (Vorlayout) mit den anderen Methoden einstellen, müssen Sie die Größe von THE VIEW und nicht des Bildschirmgeräts berechnen. Sie stimmen nur überein, wenn Sie mit Ihrer Karte im Vollbildmodus arbeiten und keine Fragmente verwenden.


Bis jetzt habe ich dein kleines Snippet benutzt, aber es
schlägt

1
Wie / wann scheitert es? hatte nie ein Problem damit. Bei der ersten Antwort wird lediglich eine fest codierte Größe als Fallback übergeben. "Es funktioniert" wie in "Es stürzt nicht mehr ab", aber es macht nicht dasselbe.
Daniele Segato

@ andrea.spot Ich erinnere mich nicht, warum ich einfacher gesagt habe. Ich denke, die akzeptierte Lösung wurde inzwischen bearbeitet. Die derzeit akzeptierte Lösung setzt voraus, dass Ihre Karte den gesamten Bildschirm einnimmt, was nicht immer der Fall ist. Wenn Sie in diesem Fall nicht fallen, müssen Sie entweder die Größe der Karte kennen oder meine Methode oder ähnliches verwenden.
Daniele Segato

Überprüfen Sie auch die Antwort unten von Xingang Huang, es fügt ein bisschen hinzu.
Daniele Segato

15

Dies ist mein Fix, warten Sie einfach, bis die Karte in diesem Fall geladen ist.

final int padding = getResources().getDimensionPixelSize(R.dimen.spacing);    

try {
    mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
} catch (IllegalStateException ise) {

    mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {

        @Override
        public void onMapLoaded() {
            mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
        }
    });
}

Einfach und effizient +1
Shid

14

Das Hinzufügen und Entfernen von Markierungen kann vor Abschluss des Layouts erfolgen, das Bewegen der Kamera jedoch nicht (außer newLatLngBounds(boundary, padding)wie in der Antwort des OP angegeben).

Der wahrscheinlich beste Ort, um ein erstes Kamera-Update durchzuführen, ist die Verwendung einer einmaligen Aufnahme, OnGlobalLayoutListenerwie im Beispielcode von Google gezeigt. Beispiel: Siehe folgenden Auszug setUpMap()in MarkerDemoActivity.java :

// 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.getViewTreeObserver().isAlive()) {
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
        @SuppressLint("NewApi") // We check which build version we are using.
        @Override
        public void onGlobalLayout() {
            LatLngBounds bounds = new LatLngBounds.Builder()
                    .include(PERTH)
                    .include(SYDNEY)
                    .include(ADELAIDE)
                    .include(BRISBANE)
                    .include(MELBOURNE)
                    .build();
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
              mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
              mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
        }
    });
}


Ich erhalte diesen Fehler bei einer Orientierungsänderung. Ich habe dies auch anhand des Beispielcodes gefunden, aber das hat bei mir nicht funktioniert. Ich bekomme immer noch den gleichen Fehler.
osrl

14

Die akzeptierte Antwort würde für mich nicht funktionieren, da ich an verschiedenen Stellen meiner App denselben Code verwenden musste.

Anstatt darauf zu warten, dass sich die Kamera ändert, habe ich eine einfache Lösung erstellt, die auf Lees Vorschlag basiert, die Kartengröße anzugeben. Dies ist der Fall, wenn Ihre Karte die Größe des Bildschirms hat.

// Gets screen size
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
// Calls moveCamera passing screen size as parameters
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, 10));

Hoffe es hilft jemand anderem!


8

Die akzeptierte Antwort ist, wie in den Kommentaren ausgeführt, etwas hackig. Nach der Implementierung habe ich eine überdurchschnittliche Anzahl von IllegalStateExceptionProtokollen in Analytics festgestellt . Die Lösung, die ich verwendet habe, besteht darin, eine hinzuzufügen, OnMapLoadedCallbackin der das ausgeführt CameraUpdatewird. Der Rückruf wird dem hinzugefügt GoogleMap. Nachdem Ihre Karte vollständig geladen wurde, wird das Kamera-Update durchgeführt.

Dies führt dazu, dass auf der Karte kurz die verkleinerte Ansicht (0,0) angezeigt wird, bevor die Kamera aktualisiert wird. Ich bin der Meinung, dass dies akzeptabler ist, als Abstürze zu verursachen oder sich auf undokumentiertes Verhalten zu verlassen.


5
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));

Verwenden Sie dies, es hat für mich funktioniert


Dadurch wird nur die Karte zentriert, der Zoom jedoch nicht dynamisch an die Grenzen angepasst.
Giovanni Di Gregorio

Mit "LatLngBounds.Builder" können wir Grenzen für mehr Matker berechnen.
Ramesh Bhupathi

@RameshBhupathi, aber es bleibt weiterhin eine Kartenansicht mit einer Mitte und einem Zoom.
Tima

4

In sehr seltenen Fällen ist das MapView-Layout fertig, aber das GoogleMap-Layout ist noch nicht fertig. ViewTreeObserver.OnGlobalLayoutListenerDer Absturz kann selbst nicht gestoppt werden. Ich habe den Absturz mit der Google Play-Dienstpaketversion 5084032 gesehen. Dieser seltene Fall kann durch die dynamische Änderung der Sichtbarkeit meiner MapView verursacht werden.

Um dieses Problem zu lösen, habe ich eingebettet GoogleMap.OnMapLoadedCallbackin onGlobalLayout():

if (mapView.getViewTreeObserver().isAlive()) {
    mapView.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        @SuppressWarnings("deprecation")
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
            try {
                map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 5));
            } catch (IllegalStateException e) {
                map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
                    @Override
                    public void onMapLoaded() {
                        Log.d(LOG_TAG, "move map camera OnMapLoadedCallback");
                        map.moveCamera(CameraUpdateFactory
                            .newLatLngBounds(bounds, 5));
                    }
                });
            }
        }
    });
}

Interessant: Wenn ich mapView.getViewTreeObserver()eine lokale Variable umgestalte, tritt der folgende Fehler auf: "IllegalStateException: Dieser ViewTreeObserver ist nicht aktiv, rufen Sie getViewTreeObserver () erneut auf" . Hast du das versucht?
JJD

Wenn mapView.getViewTreeObserver (). IsAlive () zufällig falsch ist, würde ich einen weiteren Fallback für map.setOnMapLoadedCallback () setzen
Südton

4

Ich habe einen anderen Ansatz verwendet, der für neuere Versionen von Google Maps SDK (9.6+) funktioniert und auf onCameraIdleListener basiert . Wie ich bisher sehe, wird die Rückrufmethode onCameraIdleimmer danach aufgerufen onMapReady. Mein Ansatz sieht also wie dieser Code aus (wenn man bedenkt, dass er eingegeben wurde Activity):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // set content view and call getMapAsync() on MapFragment
}

@Override
public void onMapReady(GoogleMap googleMap) {
    map = googleMap;
    map.setOnCameraIdleListener(this);
    // other initialization stuff
}

@Override
public void onCameraIdle() {
    /* 
       Here camera is ready and you can operate with it. 
       you can use 2 approaches here:

      1. Update the map with data you need to display and then set
         map.setOnCameraIdleListener(null) to ensure that further events
         will not call unnecessary callback again.

      2. Use local boolean variable which indicates that content on map
         should be updated
    */
}

4

Ich habe eine Möglichkeit geschaffen, die beiden Rückrufe zu kombinieren: onMapReady und onGlobalLayout in einem einzigen Observable, das nur ausgegeben wird, wenn beide Ereignisse ausgelöst wurden.

https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a


1
Es ist in der Tat der sauberste Weg, dies zu lösen. Ich mache das schon lange und selbst bei 1.000 Benutzern pro Tag wird kein Fehler in der Analyse zurückgegeben. +1
Skyle

2

Ok, ich stehe vor dem gleichen Problem. Ich habe mein Fragment mit meiner SupportmapFragment-, ABS- und Navigationsschublade. Was ich getan habe war:

public void resetCamera() {

    LatLngBounds.Builder builderOfBounds = new LatLngBounds.Builder();
    // Set boundaries ...
    LatLngBounds bounds = builderOfBounds.build();
    CameraUpdate cu;
    try{
        cu = CameraUpdateFactory.newLatLngBounds(bounds,10);
        // This line will cause the exception first times 
        // when map is still not "inflated"
        map.animateCamera(cu); 
        System.out.println("Set with padding");
    } catch(IllegalStateException e) {
        e.printStackTrace();
        cu = CameraUpdateFactory.newLatLngBounds(bounds,400,400,0);
        map.animateCamera(cu);
        System.out.println("Set with wh");
    }

    //do the rest...
}

Und, nebenbei bemerkt , ich rufe resetCamera()aus onCreateViewnach dem Aufblasen und vor der Rückkehr.
Dies fängt die Ausnahme beim ersten Mal ab (während die Karte "eine Größe bekommt", um es auszudrücken ...) und dann, wenn ich die Kamera zurücksetzen muss, hat die Karte bereits die Größe und führt sie durch Auffüllen aus.

Problem wird in erklärt Dokumentation , heißt es:

Ändern Sie die Kamera mit diesem Kamera-Update erst, wenn die Karte einem Layout unterzogen wurde (damit diese Methode den entsprechenden Begrenzungsrahmen und die Zoomstufe korrekt bestimmt, muss die Karte eine Größe haben). Andernfalls wird ein IllegalStateExceptionWurf geworfen. Es reicht NICHT aus, dass die Karte verfügbar ist (dh getMap()ein Nicht-Null-Objekt zurückgibt). Die Ansicht mit der Karte muss ebenfalls so gestaltet worden sein, dass ihre Abmessungen bestimmt wurden. Wenn Sie nicht sicher sind, ob dies geschehen ist, verwenden Sie newLatLngBounds(LatLngBounds, int, int, int)stattdessen und geben Sie die Abmessungen der Karte manuell an.

Ich denke, es ist eine ziemlich anständige Lösung. Hoffe es hilft jemandem.


1
Gibt es einen Listener, der zuhört, wenn das Layout vollständig geladen ist? ?
Sagar Nayak

Wird die Ausnahme in newLatLngBounds()oder in animateCamera()oder in beiden ausgelöst? Wird der Fangzweig nicht zu einer neuen Ausnahme führen?
CoolMind

1

Wenn Sie auf den Start möglicher Benutzerinteraktionen warten müssen, verwenden Sie diese OnMapLoadedCallbackwie in den vorherigen Antworten beschrieben. Wenn Sie jedoch nur einen Standardspeicherort für die Karte angeben müssen, ist keine der in diesen Antworten beschriebenen Lösungen erforderlich. Beide MapFragmentund MapViewkönnen einen GoogleMapOptionsStart akzeptieren , der den Standardspeicherort in Ordnung liefern kann. Der einzige Trick besteht darin, sie nicht direkt in Ihr Layout aufzunehmen, da das System sie dann ohne die Optionen aufruft, sondern sie dynamisch initialisiert.

Verwenden Sie dies in Ihrem Layout:

<FrameLayout
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

und ersetzen Sie das Fragment in Ihrem onCreateView():

GoogleMapOptions options = new GoogleMapOptions();
options.camera(new CameraPosition.Builder().target(location).zoom(15).build());
// other options calls if required

SupportMapFragment fragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.map);
if (fragment == null) {
  FragmentTransaction transaction = getFragmentManager().beginTransaction();
  fragment = SupportMapFragment.newInstance(options);
  transaction.replace(R.id.map, fragment).commit();
  getFragmentManager().executePendingTransactions();
}
if (fragment != null)
  GoogleMap map = fragment.getMap();

Abgesehen davon, dass der Start schneller ist, wird zuerst keine Weltkarte angezeigt und dann wird eine Kamera bewegt. Die Karte beginnt direkt mit dem angegebenen Ort.


0

Ein anderer Ansatz wäre ungefähr so: (Angenommen, Ihre oberste Ansicht ist ein FrameLayout mit dem Namen rootContainer, obwohl dies funktioniert, solange Sie immer Ihren obersten Container auswählen, unabhängig davon, welchen Typ oder Namen er hat):

((FrameLayout)findViewById(R.id.rootContainer)).getViewTreeObserver()
    .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        public void onGlobalLayout() {
            layoutDone = true;
        }
    });

Wenn Sie Ihre Kamerafunktionen so ändern, dass sie nur funktionieren, wenn dies der Fall layoutDoneist, truewerden alle Ihre Probleme gelöst, ohne dass dem layoutListenerHandler zusätzliche Funktionen hinzugefügt oder Logik verkabelt werden müssen.


0

Ich fand, dass dies funktioniert und einfacher ist als die anderen Lösungen:

private void moveMapToBounds(final CameraUpdate update) {
    try {
        if (movedMap) {
            // Move map smoothly from the current position.
            map.animateCamera(update);
        } else {
            // Move the map immediately to the starting position.
            map.moveCamera(update);
            movedMap = true;
        }
    } catch (IllegalStateException e) {
        // Map may not be laid out yet.
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                moveMapToBounds(update);
            }
        });
    }
}

Dadurch wird der Aufruf erneut ausgeführt, nachdem das Layout ausgeführt wurde. Könnte wahrscheinlich eine Sicherheit beinhalten, um eine Endlosschleife zu vermeiden.


0

Warum nicht einfach so etwas verwenden:

CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), padding);
try {
    map.moveCamera(cameraUpdate);
} catch (Exception e) {
    int width = getResources().getDisplayMetrics().widthPixels;
    int height = getResources().getDisplayMetrics().heightPixels;
    cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, padding);
    map.moveCamera(cameraUpdate);
}

Warum newLatLngBounds(builder.build(), padding)ist nicht drin Try-Catch? Tritt diese Ausnahme in auf moveCamera()?
CoolMind

0

Im Google Maps-Repo gibt es eine Hilfsklasse, die Sie nutzen können. Sie wartet darauf, dass sowohl das Layout als auch die Karte fertig sind, bevor Sie einen Rückruf mit GoogleMap benachrichtigen:

Die Originalquelle ist hier:

https://github.com/googlemaps/android-samples/blob/7ee737b8fd6d39c77f8b3716ba948e1ce3730e61/ApiDemos/java/app/src/main/java/com/example/mapdemo/OnMapAndViewReady

Es gibt auch eine Kotlin-Implementierung:

https://github.com/googlemaps/android-samples/blob/master/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt

public class OnMapAndViewReadyListener implements OnGlobalLayoutListener, OnMapReadyCallback {

/** A listener that needs to wait for both the GoogleMap and the View to be initialized. */
public interface OnGlobalLayoutAndMapReadyListener {
    void onMapReady(GoogleMap googleMap);
}

private final SupportMapFragment mapFragment;
private final View mapView;
private final OnGlobalLayoutAndMapReadyListener devCallback;

private boolean isViewReady;
private boolean isMapReady;
private GoogleMap googleMap;

public OnMapAndViewReadyListener(
        SupportMapFragment mapFragment, OnGlobalLayoutAndMapReadyListener devCallback) {
    this.mapFragment = mapFragment;
    mapView = mapFragment.getView();
    this.devCallback = devCallback;
    isViewReady = false;
    isMapReady = false;
    googleMap = null;

    registerListeners();
}

private void registerListeners() {
    // View layout.
    if ((mapView.getWidth() != 0) && (mapView.getHeight() != 0)) {
        // View has already completed layout.
        isViewReady = true;
    } else {
        // Map has not undergone layout, register a View observer.
        mapView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    // GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later.
    mapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    // NOTE: The GoogleMap API specifies the listener is removed just prior to invocation.
    this.googleMap = googleMap;
    isMapReady = true;
    fireCallbackIfReady();
}

@SuppressWarnings("deprecation")  // We use the new method when supported
@SuppressLint("NewApi")  // We check which build version we are using.
@Override
public void onGlobalLayout() {
    // Remove our listener.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    } else {
        mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }
    isViewReady = true;
    fireCallbackIfReady();
}

private void fireCallbackIfReady() {
    if (isViewReady && isMapReady) {
        devCallback.onMapReady(googleMap);
    }
}
}

0

Wie in OnCameraChangeListener () angegeben, ist es veraltet und setOnCameraChangeListenerjetzt veraltet. Sie sollten es also durch eine von drei Methoden ersetzen:

  • GoogleMap.OnCameraMoveStartedListener
  • GoogleMap.OnCameraMoveListener
  • GoogleMap.OnCameraIdleListener

In meinem Fall habe ich es benutzt OnCameraIdleListenerund innen entfernt, weil es bei jeder Bewegung immer wieder aufgerufen wurde.

googleMap.setOnCameraIdleListener {
    googleMap.setOnCameraIdleListener(null) // It removes the listener.
    googleMap.moveCamera(track)
    googleMap.cameraPosition
    clusterManager!!.cluster()
    // Add another listener to make ClusterManager correctly zoom clusters and markers.
    googleMap.setOnCameraIdleListener(clusterManager)
}

AKTUALISIEREN

Ich habe googleMap.setOnCameraIdleListenerin meinem Projekt entfernt, weil es manchmal nicht aufgerufen wurde, wenn eine Karte angezeigt wurde, sondern beibehalten wurde googleMap.setOnCameraIdleListener(clusterManager).


Vielen Dank für Ihre Antwort. Ich bin bereits dabei, die Lösung fast gleich zu finden.
Arslan Maqbool

@ ArslanMaqbool, danke! Ich bin gerade in Bearbeitung und werde Ihnen dankbar sein, wenn Sie Ihre Variante teilen.
CoolMind

mein Vergnügen @CoolMind
Arslan Maqbool

0

Ich hatte den gleichen Fehler beim Versuch, mMap.animateCamera aufzurufen. Ich habe es gelöst, indem ich setOnMapLoadedCallback- Rückruf in meiner onMapReady- Funktion aufgerufen habe , wie unten gezeigt

public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

   // other codes go there

    mMap.setOnMapLoadedCallback(() -> {
            //Your code where exception occurs goes here...
            
        });
}

Dies sollte gut funktionieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.