Intereting Posts
Сообщение об ошибке софт-панели Android Установите фон блокировки экрана в Android (например, Spotify do) Я получаю эту ошибку: «ваш процессор не поддерживает vt-x или svm, android studio 2.1.1 в процессоре AMD 6300», Как запустить Android-приложение без «android.intent.category.LAUNCHER» Gradle: разрешать зависимости только для желаемого варианта Простое присваивание с неправильным значением Фиксированные вкладки в новом дизайне для дизайна материалов Радиальный градиент в формате XML с размером родителя Android: как получить выход медиа-рекордера в формате MP3? Как вызвать onListItemClick в ListFragment Полученные сообщения GCM Не удается получить push-уведомление от Urban Airship Google Maps API v2 Android добавляет форму, которая может быть выбрана в качестве маркера Почему нет view.invalidate, чтобы сразу перерисовать экран в моей андроидной игре Изображение растягивается при отображении в закругленном изображении

MoveCamera с ошибками CameraUpdateFactory.newLatLngBounds

Я использую новый API Google Maps Google .

Я создаю действие, которое включает MapFragment. В onResume я устанавливаю маркеры в объект GoogleMap, а затем определяю ограничительную рамку для карты, которая включает все маркеры.

Это использует следующий псевдокод:

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

Вызов map.moveCamera() приводит к сбою моего приложения со следующим стеком:

 Caused by: java.lang.IllegalStateException: Map size should not be 0. Most likely, layout has not yet at maps.am.rb(Unknown Source) at maps.yqa(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) 

Если вместо метода newLatLngBounds() я использую метод newLatLngZoom() то та же ловушка не возникает.

Является ли onResume лучшим местом для рисования маркеров на объекте GoogleMap или я должен рисовать маркеры и устанавливать положение камеры в другом месте?

Solutions Collecting From Web of "MoveCamera с ошибками CameraUpdateFactory.newLatLngBounds"

Вы можете использовать простой метод newLatLngBounds в OnCameraChangeListener . Все будет работать отлично, и вам не нужно вычислять размер экрана. Это событие происходит после вычисления размера карты (как я понимаю).

Пример:

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

Эти ответы просто прекрасны, но я бы пошел на другой подход, более простой. Если этот метод работает только после того, как карта выложена, просто подождите:

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

Хорошо, я справился. Как описано здесь , API не может использоваться для предварительной компоновки.

Правильный API для использования описывается как:

Примечание. Используйте только более простой метод newLatLngBounds (граница, дополнение) для создания CameraUpdate, если он будет использоваться для перемещения камеры после того, как карта претерпела макет. Во время компоновки API вычисляет границы отображения карты, которые необходимы для правильного проектирования ограничивающего прямоугольника. Для сравнения вы можете использовать CameraUpdate, возвращенный более сложным методом newLatLngBounds (граница, ширина, высота, дополнение) в любое время, даже до того, как карта претерпела макет, потому что API вычисляет границы отображения из аргументов, которые вы передаете.

Чтобы исправить проблему, я вычислил размер экрана и предоставил ширину и высоту

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

Это позволило мне указать предварительную компоновку рамки.

Решение проще, чем это ….

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

Catch IllegalStateException и вместо этого используйте глобальный прослушиватель. Установка размера привязки заранее (предварительная компоновка) с использованием других методов означает, что вам нужно вычислить размер ПРОСМОТРА, а не экранное устройство. Они соответствуют только тому, что вы идете в полноэкранном режиме с вашей картой и не используете фрагменты.

Добавление и удаление маркеров может быть выполнено с завершением предварительной компоновки, но перемещение камеры невозможно (за исключением использования newLatLngBounds(boundary, padding) как указано в ответе OP).

Вероятно, лучшим местом для первоначального обновления камеры является использование одного выстрела OnGlobalLayoutListener как показано в образце кода Google , например, см. Следующий отрывок из setUpMap() в 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)); } }); } 

Принятый ответ, как указано в комментариях, немного взломан. После его реализации я заметил более приемлемое количество журналов IllegalStateException в аналитике. Решением, которое я использовал, является добавление OnMapLoadedCallback в котором выполняется CameraUpdate . Обратный вызов добавляется в GoogleMap . После полной загрузки карты будет выполнено обновление камеры.

Это приводит к тому, что карта будет кратковременно показывать снимок (0,0) перед выполнением обновления камеры. Я чувствую, что это более приемлемо, чем вызывать сбои или полагаться на недокументированное поведение.

Это мое исправление, просто подождите, пока карта не будет загружена в этом случае.

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

Принятый ответ не будет работать для меня, потому что мне пришлось использовать тот же код в разных местах моего приложения.

Вместо того, чтобы ждать, пока камера изменится, я создал простое решение, основанное на предложении Ли указать размер карты. Это в случае, если ваша карта является размером экрана.

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

Надеюсь, это поможет кому-то еще!

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

Используйте это, он работал для mee

В очень редких случаях макет MapView завершен, но макет GoogleMap не завершен, сам ViewTreeObserver.OnGlobalLayoutListener не может остановить сбой. Я видел сбой с пакетом услуг Google Play версии 5084032. Этот редкий случай может быть вызван динамическим изменением видимости моего MapView.

Чтобы решить эту проблему, я onGlobalLayout() GoogleMap.OnMapLoadedCallback в 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)); } }); } } }); } 

Я использовал другой подход, который работает для последних версий SDK Google Maps (9.6+) и основан на onCameraIdleListener . Как я вижу до сих пор метод callback onCameraIdle вызывался всегда после onMapReady . Поэтому мой подход похож на этот кусок кода (учитывая, что он помещен в 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 */ } 

Хорошо, я сталкиваюсь с такой же проблемой. У меня есть свой фрагмент с моей поддержкой SupportMapFragment, ABS и навигационным ящиком. Что я сделал:

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

И, кстати, я resetCamera() из onCreateView после раздувания и перед возвратом.
То, что это делает, заключается в том, чтобы сначала поймать исключение (в то время как карта «получает размер» как способ сказать это …), а затем, в другое время, мне нужно сбросить камеру, карта уже имеет размер и делает это через прописку.

Проблема объясняется в документации , в ней говорится:

Не меняйте камеру с обновлением этой камеры, пока карта не претерпела макет (чтобы этот метод правильно определял соответствующий ограничивающий прямоугольник и уровень масштабирования, карта должна иметь размер). В противном случае будет IllegalStateException . Это недостаточно для того, чтобы карта была доступна (т.е. getMap() возвращает ненулевой объект); Представление, содержащее карту, должно быть также изменено таким образом, чтобы его размеры были определены. Если вы не можете быть уверены, что это произошло, newLatLngBounds(LatLngBounds, int, int, int) используйте newLatLngBounds(LatLngBounds, int, int, int) и newLatLngBounds(LatLngBounds, int, int, int) размеры карты вручную.

Я думаю, что это довольно приличное решение. Надеюсь, это поможет кому-то.

Если вам нужно дождаться начала взаимодействия с пользователем, используйте OnMapLoadedCallback как описано в предыдущих ответах. Но если вам нужно указать местоположение карты по умолчанию, нет необходимости в каких-либо решениях, изложенных в этих ответах. И MapFragment и MapFragment могут принимать GoogleMapOptions в начале, которые могут обеспечить местоположение по умолчанию. Единственный трюк заключается не в том, чтобы включать их непосредственно в ваш макет, потому что система будет вызывать их без параметров, а затем инициализировать их динамически.

Используйте это в своем макете:

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

И замените фрагмент в вашем 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(); 

Кроме того, что вы начинаете быстрее, сначала не будет отображаться карта мира, а камера – вторая. Карта начнется с указанного местоположения напрямую.

Другой подход был бы похож на (Предполагая, что ваш самый верхний вид – это FrameLayout с именем rootContainer , хотя он будет работать до тех пор, пока вы всегда выбираете свой самый верхний контейнер независимо от того, какой тип или имя он имеет):

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

Изменение функций вашей камеры будет работать только в том случае, если layoutDone true , решит все ваши проблемы, не добавив дополнительных функций или layoutListener логику к обработчику layoutListener .

Я нашел это работать и быть более простым, чем другие решения:

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

Это повторяет вызов после выполнения макета. Вероятно, можно включить безопасность, чтобы избежать бесконечного цикла.

Я создал способ объединить два обратных вызова: onMapReady и onGlobalLayout в один единственный наблюдаемый, который будет излучать только тогда, когда оба события были инициированы.

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