moveCamera z CameraUpdateFactory.newLatLngBounds ulega awarii

96

Korzystam z nowego interfejsu API Map Google na Androida .

Tworzę działanie, które zawiera MapFragment. W ćwiczeniu onResumeustawiam znaczniki w obiekcie GoogleMap, a następnie definiuję ramkę ograniczającą dla mapy, która zawiera wszystkie znaczniki.

To używa następującego pseudokodu:

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

Wywołanie do map.moveCamera()powoduje awarię mojej aplikacji z następującym stosem:

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)

Jeśli - zamiast newLatLngBounds()metody fabrycznej używam newLatLngZoom()metody, to ta sama pułapka nie występuje.

Czy onResumenajlepiej jest rysować znaczniki na obiekcie GoogleMap, czy też powinienem rysować znaczniki i ustawiać położenie kamery w innym miejscu?

Zawietrzny
źródło

Odpowiedzi:

133

Można użyć prostego newLatLngBounds metodę w OnCameraChangeListener . Wszystko będzie działać idealnie i nie musisz obliczać rozmiaru ekranu. To zdarzenie występuje po obliczeniu rozmiaru mapy (jak rozumiem).

Przykład:

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);
    }
});
Paul Annekov
źródło
1
To działa, ponieważ setOnCameraChangeListener jest gwarantowane do uruchomienia co najmniej raz po przejściu mapy przez układ? Czy to dowód na przyszłość?
Glenn Bech
3
@SteelRat Dlatego byłbym przeciwny stosowaniu tego rozwiązania. Nigdy nie wiadomo, kiedy Google to zmieni, a nawet czy działa w ten sposób na wszystkich wersjach Androida lub urządzeniach.
Glenn Bech
1
@SteelRat Zgadzam się, ale może to być wywoływane więcej niż raz w trakcie trwania działania? Twierdzę tylko, że nawet jeśli w tym przypadku działa, to niezbyt eleganckie rozwiązanie. Myślę, że OnGlobalLayoutListener / Treelistener jest bardziej poprawnym sposobem rozwiązania tego problemu.
Glenn Bech
4
może dodanie map.moveCamera (CameraUpdateFactory.scrollBy (1, 1)); po powyższym kodzie załatwi sprawę?
MY
3
setOnCameraChangeListenerjest teraz przestarzały
osrl
56

Te odpowiedzi są w porządku, ale wybrałbym inne podejście, prostsze. Jeśli metoda działa dopiero po ułożeniu mapy, po prostu poczekaj na to:

map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
    @Override
    public void onMapLoaded() {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
    }
});
Leandro
źródło
1
Po wielu eksperymentach stwierdziłem, że muszę zastosować kombinację tej odpowiedzi i powyższej odpowiedzi Daniele. Czasami mapa nie została jeszcze załadowana, czasami układ nie został jeszcze ukończony. Nie mogłem poruszać aparatem zgodnie z życzeniem, dopóki nie wydarzyły się OBIE z nich.
RobP
2
Ostrzeżenie dotyczące tej metody z dokumentacji: „To zdarzenie nie zostanie uruchomione, jeśli mapa nigdy nie zostanie załadowana z powodu problemów z łącznością lub jeśli mapa stale się zmienia i nigdy się nie kończy, ponieważ użytkownik nieustannie korzysta z mapy ”. (podkreślenie moje).
stkent
1
Opóźnia to dłużej niż to konieczne, ponieważ czeka, aż kafelki mapy zostaną załadowane w niewłaściwej pozycji, zanim przejdą do właściwej pozycji.
John Patterson,
1
W większości przypadków wystrzelenie zajmuje od 3 do 10 sekund, co jest niedopuszczalne.
Nima G
To jest miejsce do przenoszenia aparatu. Sugerowałbym użycie map.animateCamera zamiast map.moveCamera
Bharat Dodeja
51

OK, załatwiłem to. Jak udokumentowano tutaj , API nie może być używane przed układem.

Prawidłowy interfejs API do użycia jest opisany jako:

Uwaga: Używaj tylko prostszej metody newLatLngBounds (boundary, padding), aby wygenerować CameraUpdate, jeśli ma być używana do przesuwania kamery po przejściu mapy przez układ. Podczas projektowania interfejs API oblicza granice wyświetlania mapy, które są potrzebne do prawidłowego odwzorowania ramki ograniczającej. Dla porównania, możesz użyć CameraUpdate zwróconej przez bardziej złożoną metodę newLatLngBounds (boundary, width, height, padding) w dowolnym momencie, nawet zanim mapa przeszła układ, ponieważ interfejs API oblicza granice wyświetlania na podstawie przekazanych argumentów.

Aby rozwiązać problem, obliczyłem rozmiar ekranu i podałem szerokość i wysokość

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

Dzięki temu mogłem określić wstępny układ obwiedni.

Zawietrzny
źródło
18
Ale co, jeśli mapa nie zajmuje całego ekranu?
theblang
W moim przypadku po prostu założyłem większy ekran niż w rzeczywistości i musiałem obliczyć wypełnienie dokładniej, aby w rzeczywistości nie był zbyt duży. O wiele prostszy powód.
rfay
2
Czy możesz pokazać, jak obliczyć to na podstawie rozmiaru ekranu?
Daniel Gomez Rico
To się nie zawiesza, ale szerokość i wysokość, które mają być użyte, są bliskie domysłom, a dokładny obszar do narysowania jest naprawdę nieprzewidywalny.
Vince
34

Rozwiązanie jest prostsze niż to ....

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

IllegalStateExceptionZamiast tego złap i użyj globalnego nasłuchiwania w widoku. Ustawienie rozmiaru oprawionego z wyprzedzeniem (układ wstępny) przy użyciu innych metod oznacza, że ​​musisz obliczyć rozmiar WIDOKU, a nie urządzenia ekranowego. Pasują tylko wtedy, gdy przejdziesz do pełnego ekranu z mapą i nie używasz fragmentów.

Daniele Segato
źródło
Do tej pory korzystałem z twojego małego fragmentu, ale nadal zawodzi ... prawidłowa odpowiedź jest pierwsza :-)
cesards
1
Jak / kiedy to zawiedzie? nigdy nie miałem z tym problemu. Pierwsza odpowiedź po prostu przekazuje mu zakodowany rozmiar jako rezerwę. „To działa” jak „już się nie zawiesza”, ale nie robi tego samego.
Daniele Segato
@ andrea.spot Nie pamiętam, dlaczego powiedziałem prościej. Wydaje mi się, że przyjęte rozwiązanie zostało w międzyczasie zredagowane. Prąd rozwiązania akceptowanego zakładamy mapa wziąć cały ekran, co nie zawsze jest prawdą. Jeśli nie spadniesz w takim przypadku, musisz znać rozmiar mapy lub użyć mojej metody lub czegoś podobnego.
Daniele Segato,
sprawdź także poniższą odpowiedź autorstwa Xinganga Huanga, trochę do niej dodaje.
Daniele Segato,
15

To jest moja poprawka, po prostu poczekaj, aż mapa zostanie załadowana w tym przypadku.

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));
        }
    });
}
pl3kn0r
źródło
Prosty i skuteczny +1
Shid
14

Dodawanie i usuwanie znaczników można wykonać przed ukończeniem układu, ale przesuwanie kamery nie jest możliwe (z wyjątkiem użycia newLatLngBounds(boundary, padding)opisanego w odpowiedzi OP).

Prawdopodobnie najlepsze miejsce, aby wykonać wstępną aktualizację aparatu używa jednorazowych OnGlobalLayoutListener, jak pokazano na przykładowym kodzie Google , np patrz poniższy fragment setUpMap()w 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));
        }
    });
}
StephenT
źródło
Ten błąd pojawia się przy zmianie orientacji. Znalazłem to również z przykładowego kodu, ale to nie zadziałało. Nadal pojawia się ten sam błąd.
osrl
14

Zaakceptowana odpowiedź nie zadziała, ponieważ musiałem używać tego samego kodu w różnych miejscach mojej aplikacji.

Zamiast czekać na zmianę aparatu, stworzyłem proste rozwiązanie oparte na sugestii Lee, aby określić rozmiar mapy. Dzieje się tak w przypadku, gdy mapa ma rozmiar ekranu.

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

Mam nadzieję, że pomoże to komuś innemu!

Teo Inke
źródło
8

Przyjęta odpowiedź jest, jak wskazano w komentarzach, trochę hakerska. Po jego wdrożeniu zauważyłem ponad akceptowalną ilość IllegalStateExceptionlogów w analityce. Rozwiązaniem, którego użyłem, jest dodanie, OnMapLoadedCallbackw którym CameraUpdatejest wykonywany. Wywołanie zwrotne zostanie dodane do GoogleMap. Po pełnym załadowaniu mapy zostanie wykonana aktualizacja kamery.

Powoduje to, że mapa na krótko pokazuje widok pomniejszony (0,0) przed wykonaniem aktualizacji kamery. Uważam jednak, że jest to bardziej akceptowalne niż powodowanie awarii lub poleganie na nieudokumentowanym zachowaniu.

theblang
źródło
5
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));

użyj tego to zadziałało dla mnie

Ramesh Bhupathi
źródło
Spowoduje to tylko wyśrodkowanie mapy, ale nie dynamiczne dostosowanie powiększenia do granic.
Giovanni Di Gregorio
Możemy obliczyć granice dla większej liczby dopasowań za pomocą „LatLngBounds.Builder”.
Ramesh Bhupathi
@RameshBhupathi, ale nadal pozostanie widokiem mapy ze środkiem i powiększeniem.
Tima
4

W bardzo rzadkich przypadkach układ MapView jest ukończony, ale układ GoogleMap nie jest ukończony, ViewTreeObserver.OnGlobalLayoutListenersam nie może zatrzymać awarii. Widziałem awarię z pakietem usług Google Play w wersji 5084032. Ten rzadki przypadek może być spowodowany dynamiczną zmianą widoczności mojego MapView.

Aby rozwiązać ten problem, osadziłem GoogleMap.OnMapLoadedCallbackw 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));
                    }
                });
            }
        }
    });
}
Alpha Huang
źródło
Interesujące: Jeśli dokonam refaktoryzacji mapView.getViewTreeObserver()na zmienną lokalną, pojawia się następujący błąd : „IllegalStateException: ten ViewTreeObserver nie działa, ponownie wywołaj metodę getViewTreeObserver ()” . Próbowałeś tego?
JJD
Jeśli mapView.getViewTreeObserver (). IsAlive () jest fałszywe, wstawiłbym jeszcze jedną rezerwę dla map.setOnMapLoadedCallback ()
Southerton
4

Użyłem innego podejścia, które działa dla najnowszych wersji Google Maps SDK (9.6+) i opartego na onCameraIdleListener . Jak dotąd widzę, jest to metoda wywołania zwrotnego onCameraIdlezawsze poonMapReady . Więc moje podejście wygląda tak, jak ten fragment kodu (biorąc pod uwagę, że został wstawiony 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
    */
}
Wiaczesław
źródło
4

Stworzyłem sposób na połączenie dwóch wywołań zwrotnych: onMapReady i onGlobalLayout w jedną obserwowalną, która będzie emitowana tylko wtedy, gdy oba zdarzenia zostaną wyzwolone.

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

Abhay Sood
źródło
1
W rzeczywistości jest to najczystszy sposób rozwiązania tego problemu. Robię to już od dawna i żaden błąd nie jest zwracany w analizie nawet w przypadku 1 tys. Użytkowników dziennie. +1
Skyle
2

Ok, mam ten sam problem. Mam swój fragment z moim SupportmapFragment, ABS i szufladą nawigacji. To, co zrobiłem, to:

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...
}

A tak przy okazji dzwonię resetCamera()od onCreateViewpo nadmuchaniu i przed powrotem.
To co robi to złapanie wyjątku za pierwszym razem (podczas gdy mapa "pobiera rozmiar" jako sposób na to ...), a potem, innym razem muszę zresetować kamerę, mapa ma już rozmiar i robi to przez wypełnienie.

Problem jest wyjaśniony w dokumentacji , mówi:

Nie zmieniaj kamery za pomocą tej aktualizacji kamery, dopóki mapa nie przejdzie układu (aby ta metoda poprawnie określić odpowiednią ramkę graniczną i poziom powiększenia, mapa musi mieć rozmiar). W przeciwnym razie IllegalStateExceptionzostanie wyrzucony. NIE jest wystarczające, aby mapa była dostępna (tj. getMap()Zwracała obiekt niezerowy); widok zawierający mapę również musiał zostać poddany układowi tak, aby jego wymiary zostały określone. Jeśli nie możesz być pewien, że tak się stało, użyj newLatLngBounds(LatLngBounds, int, int, int)zamiast tego i podaj wymiary mapy ręcznie.

Myślę, że to całkiem przyzwoite rozwiązanie. Mam nadzieję, że to komuś pomoże.

unmultimedio
źródło
1
czy jest jakiś odbiornik do nasłuchiwania, gdy układ jest w pełni załadowany. ?
Sagar Nayak
Czy wyjątek został zgłoszony w, newLatLngBounds()czy w animateCamera(), czy w obu? Czy gałąź catch nie doprowadzi do nowego wyjątku?
CoolMind
1

Jeśli musisz zaczekać na rozpoczęcie ewentualnych interakcji użytkownika, postępuj OnMapLoadedCallbackzgodnie z opisem w poprzednich odpowiedziach. Jeśli jednak wszystko, czego potrzebujesz, to podanie domyślnej lokalizacji mapy, nie ma potrzeby stosowania żadnego z rozwiązań przedstawionych w tych odpowiedziach. Zarówno, jak MapFragmenti MapViewmogą akceptowaćGoogleMapOptions na początku, która może zapewnić domyślną lokalizację. Jedyną sztuczką jest to, aby nie włączać ich bezpośrednio do układu, ponieważ system wywoła je wtedy bez opcji, ale zainicjować je dynamicznie.

Użyj tego w swoim układzie:

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

i zamień fragment w swoim 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();

Oprócz tego, że początek jest szybszy, mapa świata nie będzie wyświetlana jako pierwsza, a kamera będzie się poruszać jako druga. Mapa rozpocznie się bezpośrednio od określonej lokalizacji.

Gábor
źródło
0

Inne podejście mogłoby wyglądać mniej więcej tak (zakładając, że Twoim najwyższym widokiem jest nazwany FrameLayout rootContainer, nawet jeśli będzie działał tak długo, jak długo będziesz wybierać najwyższy kontener, bez względu na typ lub nazwę):

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

Modyfikowanie funkcji aparatu tak, aby działały tylko wtedy, gdy layoutDonejest true, rozwiąże wszystkie problemy bez konieczności dodawania dodatkowych funkcji lub łączenia logiki z layoutListenerprogramem obsługi.

Machinarius
źródło
0

Okazało się, że to działa i jest prostsze niż inne rozwiązania:

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

Spowoduje to ponowną próbę wywołania po uruchomieniu układu. Prawdopodobnie może zawierać zabezpieczenie, aby uniknąć nieskończonej pętli.

John Patterson
źródło
0

Dlaczego nie użyć czegoś takiego:

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);
}
Hristo Lalev
źródło
Dlaczego newLatLngBounds(builder.build(), padding)nie ma w środku try-catch? Czy ten wyjątek występuje w moveCamera()?
CoolMind
0

W repozytorium Map Google znajduje się klasa pomocnicza, z której możesz skorzystać - czeka, aż układ i mapa będą gotowe, przed powiadomieniem o wywołaniu zwrotnym za pomocą GoogleMap:

Oryginalne źródło jest tutaj:

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

Jest też implementacja Kotlin:

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);
    }
}
}
dazza5000
źródło
0

Jak stwierdzono w OnCameraChangeListener () jest przestarzała , setOnCameraChangeListenerjest teraz przestarzała. Dlatego należy go zastąpić jedną z trzech metod:

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

W moim przypadku użyłem OnCameraIdleListeneri usunąłem go w środku, ponieważ był wywoływany wielokrotnie przy każdym ruchu.

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

AKTUALIZACJA

Usunąłem googleMap.setOnCameraIdleListenerw swoim projekcie, ponieważ czasami nie był wywoływany, gdy mapa była wyświetlana, ale zachowana googleMap.setOnCameraIdleListener(clusterManager).

CoolMind
źródło
dziękuję za twoją odpowiedź. już wymyślam rozwiązanie prawie takie samo.
Arslan Maqbool
@ArslanMaqbool, dzięki! Jestem teraz w trakcie i będę wdzięczny, jeśli podzielisz się swoim wariantem.
CoolMind
my pleasure @CoolMind
Arslan Maqbool
0

Wystąpił ten sam błąd podczas próby wywołania mMap.animateCamera. Rozwiązałem to, wywołując callback setOnMapLoadedCallback w mojej funkcji onMapReady, jak pokazano poniżej

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

   // other codes go there

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

To powinno działać dobrze.

Paul Kumchi
źródło