【问题标题】:animateCamera works and moveCamera doesn't for GoogleMap - AndroidanimateCamera 有效,而 moveCamera 不适用于 GoogleMap - Android
【发布时间】:2019-06-07 21:59:31
【问题描述】:

我需要移动相机以覆盖其上的所有标记。所以,我构建了LatLngBounds,然后尝试调用mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(latLngBounds, 15))。问题是当我使用moveCamera() 方法时,我得到IllegalStateException,但是当我使用animateCamera() 时,它运行得很好。我在onMapReady 回调中调用了这两种方法。怎么回事?

我的堆栈跟踪(主要部分):

java.lang.IllegalStateException: Error using newLatLngBounds(LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view.  Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions.

怎么可能一种方法知道地图大小而另一种方法不知道?

【问题讨论】:

    标签: android google-maps illegalstateexception


    【解决方案1】:

    根据documentation ,在地图经过布局之前不能使用此API。它说

    注意:只使用更简单的方法 newLatLngBounds(boundary, padding) 生成一个 CameraUpdate 如果它将用于移动 地图经过布局后的相机。在布局期间,API 计算需要的地图显示边界 正确投影边界框。相比之下,您可以使用 更复杂的方法返回的 CameraUpdate newLatLngBounds(boundary, width, height, padding) 任何时候,甚至 在地图进行布局之前,因为 API 会计算 显示您传递的参数的边界。

    但是你可以在OnCameraChangeListener 中使用newLatLngBounds() 方法。一切都会完美运行,您无需计算屏幕尺寸。据我所知,此事件发生在地图大小计算之后。

        mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
    
        @Override
        public void onCameraChange(CameraPosition arg0) {
            // Move camera.
            mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 15));
            // Remove listener to prevent position reset on camera move.
            mMap.setOnCameraChangeListener(null);
        }
    });
    

    【讨论】:

    • 您能解释一下为什么 animateCamera 有效而 moveCamera 无效吗?
    【解决方案2】:

    自从 Google Maps SDK 更新到最新版本后,onCameraChangeListener 已被弃用。我也遇到过这个问题,我发现onCameraIdleListener 做了类似的把戏。正如我目前所看到的,它的回调方法onCameraIdle 总是在onMapReady 之后调用。所以我的方法看起来像 Google Maps SDK 9.6+ 的这段代码(考虑到它放在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
        */
    }
    

    【讨论】:

    • 正是我最终所做的。完美运行
    • @Justin 很难说为什么没有任何代码示例。就我而言,我可以确认它适用于最新的 MapsAPI。
    【解决方案3】:

    这里清楚地记录了OnMapReadyCallback

    请注意,OnMapReadyCallback 不保证地图已经过布局。因此,调用回调方法的时间可能尚未确定地图的大小。如果需要知道维度或者调用API中需要知道维度的方法,获取地图的View,同时注册一个ViewTreeObserver.OnGlobalLayoutListener。

    不要链接 OnMapReadyCallback 和 OnGlobalLayoutListener 侦听器,而是独立注册并等待这两个回调,因为回调可以按任何顺序触发。

    因此,您必须同时使用 (onMapReady,onGlobalLayout) 回调来确保地图已完全加载并已确定大小。

    private GoogleMap mMap;
    private boolean isMapLoaded;
    
    SupportMapFragment mapFragment = (SupportMapFragment)getSupportFragmentManager()
                .findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);
    mapFragment.getView().getViewTreeObserver().addOnGlobalLayoutListener(this);
    
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        if (!isMapLoaded) {
            isMapLoaded = true;
            return;
        }
        initMap();
    }
    
    @Override
    public void onGlobalLayout() {
        if (!isMapLoaded) {
            isMapLoaded = true;
            return;
        }
        initMap();
    }
    
    private void initMap() {
       //maps fully loaded instance with defined size will be available here.
       //mMap.animateCamera();
       //mMap.moveCamera();
    }
    

    【讨论】:

    • 你的例子很有价值,但是如果在地图准备好之前布局布局,你怎么做onGlobalLayout()中的initMap()?抱歉,如果我错了,但我相信这应该有一个if (mMap != null)initMap() 之前签入onGlobalLayout
    • 只是一个预防措施,如果使用setOnMapLoadedCallback(),请删除它,如果您使用建议的配置,它会进入一个循环
    • 感谢您的提示。我尝试了您的代码,添加了removeOnGlobalLayoutListener (stackoverflow.com/a/15162928/2914140)。在我的情况下,onGlobalLayoutonMapReady 之前被调用。查看@Viacheslav 的答案,设置setOnCameraIdleListener(并在使用后清除)。
    【解决方案4】:

    感谢@Viacheslav 我在setOnCameraIdleListener 中做了同样的事情:

    override fun onMapReady(googleMap: GoogleMap?) {
        this.googleMap = googleMap
    
        // setupMap()
    
        googleMap?.setOnCameraIdleListener {
            // Remove the listener to stop calling the same event.
            googleMap.setOnCameraIdleListener(null)
    
            // Now you can use 'moveCamera'.
            // I also added a delay of 100 ms here in order to draw the map 
            // and correctly calculate distances. If this is your case, then add a short pause.
            val position = LatLng(latitude, longitude)
            val camera = CameraUpdateFactory.newLatLngZoom(position, 10)
            // Strange, but it doesn't work for
            // val camera = CameraUpdateFactory.zoomTo(10)
            googleMap.moveCamera(camera)
    
            // If you later want to listen to camera movements (start-stop),
            // you should change setOnCameraIdleListener here.
            googleMap.setOnCameraIdleListener{
                // A listener for future camera stops.
                ...
            }
        }
    }
    

    setOnCameraIdleListener 是一个合适的监听器来移动相机和计算距离。您也可以在 onMapReady 中通过延迟(100-300 毫秒)获得相同的效果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多