Android 使用CameraUpdateFactory.newLatLngBounds移动摄像头崩溃

Android 使用CameraUpdateFactory.newLatLngBounds移动摄像头崩溃,android,android-mapview,google-maps-android-api-2,Android,Android Mapview,Google Maps Android Api 2,我正在利用新的 我创建了一个包含MapFragment的活动。在活动onResume中,我将标记设置到GoogleMap对象中,然后为包含所有标记的地图定义一个边界框 这将使用以下伪代码: LatLngBounds.Builder builder = new LatLngBounds.Builder(); while(data) { LatLng latlng = getPosition(); builder.include(latlng); } CameraUpdate camer

我正在利用新的

我创建了一个包含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)
如果我使用
newLatLngZoom()
方法代替
newLatLngBounds()
工厂方法,则不会出现相同的陷阱

是将标记绘制到Google地图对象上的最佳位置,还是我应该在其他地方绘制标记并设置相机位置?

好的,我已经解决了这个问题。因为该API不能用于预布局

要使用的正确API如下所述:

注意:仅使用更简单的方法newLatLngBounds(边界、填充) 如果要用于移动相机,则生成相机更新 相机在地图经过布局之后。在布局期间,API 计算需要显示的地图显示边界 正确投影边界框。相比之下,您可以使用 由更复杂的方法返回的CameraUpdate 随时更新边界(边界、宽度、高度、填充),甚至 在地图经过布局之前,因为API计算 显示传递的参数的边界

为了解决这个问题,我计算了我的屏幕大小,并提供了屏幕的宽度和高度

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

然后,这允许我指定边界框的预布局。

您可以在onCamerachenglistener中使用简单的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);
    }
});

添加和删除标记可以在布局完成之前完成,但移动相机不能(除非使用
newLatLngBounds(边界,填充)
,如OP的回答所述)

可能执行初始相机更新的最佳位置是使用中所示的单拍
OnGlobalLayoutListener
,例如,请参见MarkerDemoActivity.java中
setUpMap()
的以下摘录:

//平移以查看视图中的所有标记。
//在贴图具有大小之前,无法缩放到边界。
最终视图mapView=getSupportFragmentManager()
.findFragmentById(R.id.map).getView();
如果(mapView.getViewTreeObserver().isAlive()){
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
新OnGlobalLayoutListener(){
@SuppressLint(“NewApi”)//我们检查正在使用的构建版本。
@凌驾
公共图书馆{
LatLngBounds bounds=新建LatLngBounds.Builder()
.包括(珀斯)
.包括(悉尼)
.包括(阿德莱德)
.包括(布里斯班)
.包括(墨尔本)
.build();
if(Build.VERSION.SDK\u INT
另一种方法类似于(假设最顶端的视图是名为
rootContainer
的框架布局,即使只要始终选择最顶端的容器,无论其类型或名称如何,它都可以工作):


如果将摄像头功能修改为仅在
layoutDone
true
时工作,则无需向
layoutdener
处理程序添加额外的功能或连接逻辑即可解决所有问题。

解决方案比这更简单

// 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));
            }
        });
    }
}
//平移以查看视图中的所有标记。
试一试{
这个.gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds,50));
}捕获(非法状态){
//布局尚未初始化
最终视图mapView=getFragmentManager()
.findFragmentById(R.id.map).getView();
如果(mapView.getViewTreeObserver().isAlive()){
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
新OnGlobalLayoutListener(){
@抑制警告(“弃用”)
@SuppressLint(“新API”)
//我们检查正在使用的构建版本。
@凌驾
公共图书馆{
if(Build.VERSION.SDK\u INT
捕获非法状态异常,并在视图上使用全局侦听器。
使用其他方法预先设置绑定大小(预布局)意味着您必须计算视图的大小,而不是屏幕设备的大小。只有在全屏显示地图并且不使用片段时,它们才匹配。

好的,我面临同样的问题。我有我的片段和我的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...
}
顺便说一句,我正在充气后返回之前从
onCreateView
调用
resetCamera()

这样做的目的是第一次捕获异常(而map“获取一个大小”作为一种表达方式…),然后,其他时候我需要重置相机,map已经有了大小并通过填充来完成

这一问题的解释如下:

在地图显示之前,请勿使用此相机更新更改相机 已完成布局(在ord中)
// 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));
            }
        });
    }
}
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...
}
map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
    @Override
    public void onMapLoaded() {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
    }
});
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));
                    }
                });
            }
        }
    });
}
<FrameLayout
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
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();
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));
        }
    });
}
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));
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);
            }
        });
    }
}
// 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));
@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
    */
}
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);
}
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)
}
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);
    }
}
}
public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

   // other codes go there

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