我正在使用新的Android版谷歌地图API 。
我创建活动,其中包括一个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.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)
如果-而不是newLatLngBounds()
工厂方法我用newLatLngZoom()
方法,然后同样的陷阱不会发生。
是onResume
画出标记到GoogleMap对象最好的地方或者我应该绘制标记和设置相机位置别的地方?
Answer 1:
您可以在OnCameraChangeListener用简单的方法newLatLngBounds。 一切都将被完美地工作,你不需要计算的屏幕尺寸。 此事件映射大小计算之后发生(如我明白)。
例:
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);
}
});
Answer 2:
这些问题的答案都不错,但我会去不同的方法,简单的一个。 如果地图奠定了以后的方法仅适用了,就等着它:
map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
}
});
Answer 3:
OK我工作了这一点。 由于这里记录的是API,不能使用预布局。
正确的API来使用被描述为:
注:仅使用简单的方法newLatLngBounds(边界,填充)如果是将要使用的地图已经布局发生后移动相机生成的CameraUpdate。 在布局过程中,该API计算这是需要正确投影边框地图的显示边界。 相比较而言,可以使用通过在任何时间更复杂的方法newLatLngBounds(边界,宽度,高度,填充)返回的CameraUpdate,甚至在地图已布局发生了,因为API从您传递参数计算显示边界。
要解决这个问题,我计算我的屏幕尺寸和所提供的宽度和高度
public static CameraUpdate newLatLngBounds(
LatLngBounds bounds, int width, int height, int padding)
这就让我指定边框预布局。
Answer 4:
该解决方案是简单得多....
// 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));
}
});
}
}
抓住IllegalStateException
和使用全局监听器的视图来代替。 设定使用的其它方法预先(预布局)结合的大小意味着你必须计算视图,未显示在屏幕装置的尺寸。 他们只如果你去全屏地图,不使用片段匹配。
Answer 5:
添加和删除标记可以做到布局前完成,但移动相机不能(除使用newLatLngBounds(boundary, padding)
作为OP的答案说明)。
可能是最好的地方执行初始相机更新是使用一步法OnGlobalLayoutListener
如图谷歌的样本代码 ,例如,参见由以下摘录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));
}
});
}
Answer 6:
这是我的修正,只是等待,直到地图是在这种情况下加载。
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));
}
});
}
Answer 7:
接受的答案也不会适合我,因为我不得不使用相同的代码在我的应用程序的各个地方。
与其等待相机改变,我创建了一个基于指定地图的大小李的建议,一个简单的解决方案。 这是你的情况下图是屏幕的大小。
// 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));
希望它可以帮助别人!
Answer 8:
接受的答案是,正如评论,有点哈克指出。 实现它后我注意到的上述可接受量IllegalStateException
日志中分析。 我所使用的溶液是添加OnMapLoadedCallback
其中CameraUpdate
被执行。 回调被添加到GoogleMap
。 在您的地图加载全部会进行相机更新。
这确实导致地图短暂显示执行相机更新之前所述缩小(0,0)视图。 我觉得这是不是导致崩溃或依靠无证行为,但更容易接受。
Answer 9:
map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));
使用它为MEE
Answer 10:
在极少数情况下,MapView的布局已经完成,但是GoogleMap的布局还没有完成, ViewTreeObserver.OnGlobalLayoutListener
本身不能阻止崩溃。 我看到了与谷歌Play服务包版本5084032.崩溃这种罕见的情况下,可以通过我的MapView的可见性的动态变化而引起的。
为了解决这个问题,我的嵌入式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));
}
});
}
}
});
}
Answer 11:
我用不同的方法,其适用于最新版本的谷歌地图SDK(9.6+)和基于的onCameraIdleListener 。 当我看到到目前为止,它的回调方法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
*/
}
Answer 12:
我创建了一个方式给两个回调结合:onMapReady和onGlobalLayout成一个单一可观察当这两个事件被触发,将只能发出。
https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a
Answer 13:
好吧,我面临着同样的问题。 我有我的片段与我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)
代替手动提供地图的尺寸。
我认为这是一个相当不错的解决方案。 希望它可以帮助别人。
Answer 14:
如果您需要等待可能的用户交互来启动,使用OnMapLoadedCallback
如前面的答案中描述。 但是,如果你需要的是提供地图的默认位置,没有必要对任何在这些答案中列出的解决方案。 无论MapFragment
和MapView
可以接受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();
除了是更快的启动,将有第一次显示没有的世界地图和摄像机移动第二。 该地图将直接与指定的位置开始。
Answer 15:
另一种方法是类似的东西(假设你的最顶层的看法是一个名为的FrameLayout rootContainer
,即使它会工作,只要你总是选择最上面的容器,不管它有哪些类型或名称):
((FrameLayout)findViewById(R.id.rootContainer)).getViewTreeObserver()
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
layoutDone = true;
}
});
修改你的相机功能,如果只有工作layoutDone
是true
会解决所有的问题,而无需额外的功能或线逻辑电路添加到layoutListener
处理程序。
Answer 16:
我发现这个工作,比其他解决方案更简单:
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);
}
});
}
}
这将尝试布局已运行后再次呼叫。 很可能包括安全,以避免无限循环。
Answer 17:
为什么不使用这样的事情:
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);
}
Answer 18:
正如指出OnCameraChangeListener()已被弃用 , setOnCameraChangeListener
现在已经过时。 所以,你应该有三个mtehods一个替换:
- GoogleMap.OnCameraMoveStartedListener
- GoogleMap.OnCameraMoveListener
- GoogleMap.OnCameraIdleListener
在我来说,我使用OnCameraIdleListener
和里面我删除它,因为它一次又一次地被调用的任何运动。
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)
}
UPDATE
我删除googleMap.setOnCameraIdleListener
在我的项目,因为当被出示地图这不是有时也被称为,但保留googleMap.setOnCameraIdleListener(clusterManager)
Answer 19:
有一个在谷歌地图回购一个辅助类,你可以利用 - 等待双方的布局和映射通知与GoogleMap的回调之前做好准备:
最初来源是在这里:
https://github.com/googlemaps/android-samples/blob/7ee737b8fd6d39c77f8b3716ba948e1ce3730e61/ApiDemos/java/app/src/main/java/com/example/mapdemo/OnMapAndViewReadyListener.java
有一个科特林实现过:
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);
}
}
}
文章来源: moveCamera with CameraUpdateFactory.newLatLngBounds crashes