谷歌地图API v2:从摄像机位置获取LatLngBounds

43

有没有一种简单的方法可以通过Android Google Maps API v2中的CameraPosition获取可见地图的LatLngBounds,以便我可以使用OnCameraChangeListener获取标记的新数据。

mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
            @Override
            public void onCameraChange(CameraPosition position) {
                LatLngBounds bounds = ?;
                fetchNewData(bounds);
            }
        });
2个回答

80

您无法从CameraPosition获取LatLngBounds,但您可以轻松地从GoogleMap获取它们。

private GoogleMap mMap;

mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
            @Override
            public void onCameraChange(CameraPosition position) {
                LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
                fetchData(bounds);
            }
        });

我想知道为什么他们设置成那样。可见区域与地图投影无关。 - capdragon
4
等一下,什么?地图投影恰好定义了定义可见区域的4个坐标。 - h4lc0n
1
我想说的是它们不应该像那样耦合。可见区域只是边界,无论它在什么投影中。无论投影是EPSG:4326还是EPSG:900913,视图都是相同的,无论它是在纬度/经度坐标系还是米x/y坐标系中。因此,人们应该能够独立地执行mMap.getProjection()mMap.getVisibleRegion() - capdragon
@h4lc0n:OpenLayers的方式更有意义。地图对象允许您独立调用这两个函数。map.getExtent(); map.getProjection(); - capdragon
3
在这个上下文中,“投影”与世界到屏幕的投影有关(而非地图投影),并将考虑相机的旋转和倾斜。 - Joel Malone

42

自2016年8月以来的更新

概述:现在这个问题的正确答案是使用新的onCameraIdle,而不是现在已弃用OnCameraChangeListener。请阅读下面的内容。

现在你可以在最新版本的Google Maps for Android上监听“dragEnd”等事件,甚至其他事件。

文档所示,您可以通过使用新的侦听器来避免OnCameraChangeListener多次(也称为“几次”)调用的问题。例如,现在您能够检查相机移动背后的原因,这是与请求的fetchData问题相结合的理想选择。以下代码大部分直接来自文档。还有一件事,必须使用Google Play Services 9.4

public class MyCameraActivity extends FragmentActivity implements
        OnCameraMoveStartedListener,
        OnCameraMoveListener,
        OnCameraMoveCanceledListener,
        OnCameraIdleListener,
        OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_camera);

        SupportMapFragment mapFragment =
            (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap map) {
        mMap = map;

        mMap.setOnCameraIdleListener(this);
        mMap.setOnCameraMoveStartedListener(this);
        mMap.setOnCameraMoveListener(this);
        mMap.setOnCameraMoveCanceledListener(this);

        // Show Sydney on the map.
        mMap.moveCamera(CameraUpdateFactory
                .newLatLngZoom(new LatLng(-33.87365, 151.20689), 10));
    }

    @Override
    public void onCameraMoveStarted(int reason) {

        if (reason == OnCameraMoveStartedListener.REASON_GESTURE) {
            Toast.makeText(this, "The user gestured on the map.",
                           Toast.LENGTH_SHORT).show();
        } else if (reason == OnCameraMoveStartedListener
                                .REASON_API_ANIMATION) {
            Toast.makeText(this, "The user tapped something on the map.",
                           Toast.LENGTH_SHORT).show();
        } else if (reason == OnCameraMoveStartedListener
                                .REASON_DEVELOPER_ANIMATION) {
            Toast.makeText(this, "The app moved the camera.",
                           Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onCameraMove() {
        Toast.makeText(this, "The camera is moving.",
                       Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCameraMoveCanceled() {
        Toast.makeText(this, "Camera movement canceled.",
                       Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCameraIdle() {
        Toast.makeText(this, "The camera has stopped moving. Fetch the data from the server!", Toast.LENGTH_SHORT).show();
        LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;
        fetchData(bounds)
    }
}

在2016年8月之前寻找高效解决方案的解决方法

问题已经得到妥善解答,我想补充一下可能出现的下一个问题。

当使用OnCameraChangeListener从服务器获取数据时,由于此方法被触发的频率,问题就会出现。

有一个问题报告表明,在进行简单的地图滑动时,该方法被触发的频率非常高,因此在问题的示例中,它将为非常少的相机更改甚至没有相机更改多次触发fetchData,是的,摄像头范围没有改变,但该方法仍然被触发。

这可能会影响服务器端性能,并且会浪费大量设备资源,从服务器顺序获取数据十几次。

您可以在该链接中找到解决此问题的解决方法,但尚没有官方方法来解决它,例如使用理想的dragEndcameraChangeEnd回调。

下面是一个例子,基于这些例子,我通过调整调用的时间间隔并且筛除具有相同范围的调用来避免上述问题。
// Keep the current camera bounds
private LatLngBounds currentCameraBounds;

new GoogleMap.OnCameraChangeListener() {
    private static int CAMERA_MOVE_REACT_THRESHOLD_MS = 500;
    private long lastCallMs = Long.MIN_VALUE;

    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
      LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
      // Check whether the camera changes report the same boundaries (?!), yes, it happens
      if (currentCameraBounds.northeast.latitude == bounds.northeast.latitude
         && currentCameraBounds.northeast.longitude == bounds.northeast.longitude
         && currentCameraBounds.southwest.latitude == bounds.southwest.latitude
         && currentCameraBounds.southwest.longitude == bounds.southwest.longitude) {
         return;
       }

      final long snap = System.currentTimeMillis();
      if (lastCallMs + CAMERA_MOVE_REACT_THRESHOLD_MS > snap) {
        lastCallMs = snap;
        return;
      }

      fetchData(bounds);

      lastCallMs = snap;
      currentCameraBounds = bounds;

}

非常好!这可能节省了我几个小时的工作时间,理解并解决这个问题。谢谢! - EZDsIt
你还应该检查 currentCameraBounds 字段是否为 null - Ionut Negru
onCameraIdle 被自动调用了,尽管我甚至没有触摸地图,有人能帮帮我吗? - Kaushik
@Kaushik 在 onCameraMoveStarted 期间存储“reason”,如果它不是 REASON_GESTURE,则在 onCameraIdle 上放弃调用。这样做是为了在用户手势发起的空闲事件中对其进行反应。 - Eduardo
@Eduardo 谢谢,我解决了我的问题。我犯了一个新手错误,是在onCameraIdle中调用CameraUpdate location = CameraUpdateFactory.newLatLngZoom(latlng, 12); mMap.animateCamera(location);,这使它变成了递归。 - Kaushik
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接