Korzystam z nowego interfejsu API Map Google na Androida .
Tworzę działanie, które zawiera MapFragment. W ćwiczeniu onResume
ustawiam 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 onResume
najlepiej jest rysować znaczniki na obiekcie GoogleMap, czy też powinienem rysować znaczniki i ustawiać położenie kamery w innym miejscu?
źródło
setOnCameraChangeListener
jest teraz przestarzałyTe 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:
źródło
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:
Aby rozwiązać problem, obliczyłem rozmiar ekranu i podałem szerokość i wysokość
Dzięki temu mogłem określić wstępny układ obwiedni.
źródło
Rozwiązanie jest prostsze niż to ....
IllegalStateException
Zamiast 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.źródło
To jest moja poprawka, po prostu poczekaj, aż mapa zostanie załadowana w tym przypadku.
źródło
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 fragmentsetUpMap()
w MarkerDemoActivity.java :źródło
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.
Mam nadzieję, że pomoże to komuś innemu!
źródło
Przyjęta odpowiedź jest, jak wskazano w komentarzach, trochę hakerska. Po jego wdrożeniu zauważyłem ponad akceptowalną ilość
IllegalStateException
logów w analityce. Rozwiązaniem, którego użyłem, jest dodanie,OnMapLoadedCallback
w którymCameraUpdate
jest wykonywany. Wywołanie zwrotne zostanie dodane doGoogleMap
. 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.
źródło
użyj tego to zadziałało dla mnie
źródło
W bardzo rzadkich przypadkach układ MapView jest ukończony, ale układ GoogleMap nie jest ukończony,
ViewTreeObserver.OnGlobalLayoutListener
sam 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.OnMapLoadedCallback
wonGlobalLayout()
,źródło
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?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
onCameraIdle
zawsze poonMapReady
. Więc moje podejście wygląda tak, jak ten fragment kodu (biorąc pod uwagę, że został wstawionyActivity
):źródło
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
źródło
Ok, mam ten sam problem. Mam swój fragment z moim SupportmapFragment, ABS i szufladą nawigacji. To, co zrobiłem, to:
A tak przy okazji dzwonię
resetCamera()
odonCreateView
po 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:
Myślę, że to całkiem przyzwoite rozwiązanie. Mam nadzieję, że to komuś pomoże.
źródło
newLatLngBounds()
czy wanimateCamera()
, czy w obu? Czy gałąź catch nie doprowadzi do nowego wyjątku?Jeśli musisz zaczekać na rozpoczęcie ewentualnych interakcji użytkownika, postępuj
OnMapLoadedCallback
zgodnie 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, jakMapFragment
iMapView
mogą 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:
i zamień fragment w swoim
onCreateView()
: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.
źródło
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ę):Modyfikowanie funkcji aparatu tak, aby działały tylko wtedy, gdy
layoutDone
jesttrue
, rozwiąże wszystkie problemy bez konieczności dodawania dodatkowych funkcji lub łączenia logiki zlayoutListener
programem obsługi.źródło
Okazało się, że to działa i jest prostsze niż inne rozwiązania:
Spowoduje to ponowną próbę wywołania po uruchomieniu układu. Prawdopodobnie może zawierać zabezpieczenie, aby uniknąć nieskończonej pętli.
źródło
Dlaczego nie użyć czegoś takiego:
źródło
newLatLngBounds(builder.build(), padding)
nie ma w środku try-catch? Czy ten wyjątek występuje wmoveCamera()
?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
źródło
Jak stwierdzono w OnCameraChangeListener () jest przestarzała ,
setOnCameraChangeListener
jest 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
OnCameraIdleListener
i usunąłem go w środku, ponieważ był wywoływany wielokrotnie przy każdym ruchu.AKTUALIZACJA
Usunąłem
googleMap.setOnCameraIdleListener
w swoim projekcie, ponieważ czasami nie był wywoływany, gdy mapa była wyświetlana, ale zachowanagoogleMap.setOnCameraIdleListener(clusterManager)
.źródło
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
To powinno działać dobrze.
źródło