I need to move Camera to cover all Markers on it. So, I build LatLngBounds
and then try to call mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(latLng
Thanks to @Viacheslav I made the same in 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
is an appropriate listener to move camera and calculate distances. You can also get the same with delay (100-300 ms) inside onMapReady
.
As per documentation , this API can't be used before the map has undergone layout. It says
Note: Only use the simpler method newLatLngBounds(boundary, padding) to generate a CameraUpdate if it is going to be used to move the camera after the map has undergone layout. During layout, the API calculates the display boundaries of the map which are needed to correctly project the bounding box. In comparison, you can use the CameraUpdate returned by the more complex method newLatLngBounds(boundary, width, height, padding) at any time, even before the map has undergone layout, because the API calculates the display boundaries from the arguments that you pass.
But you can make use of newLatLngBounds()
method in OnCameraChangeListener
. Everything will work perfectly and you don't need to calculate screen size. As far as I know, this event occurs after map size calculation.
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);
}
});
As clearly documented here OnMapReadyCallback
Note that OnMapReadyCallback does not guarantee that the map has undergone layout. Therefore, the map's size may not have been determined by the time the callback method is called. If you need to know the dimensions or call a method in the API that needs to know the dimensions, get the map's View and register an ViewTreeObserver.OnGlobalLayoutListener as well.
Do not chain the OnMapReadyCallback and OnGlobalLayoutListener listeners, but instead register and wait for both callbacks independently, since the callbacks can be fired in any order.
So you have to use both (onMapReady,onGlobalLayout) callbacks to make sure that map have been fully loaded and size has been determined.
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();
}
Since Google Maps SDK was updated to recent versions onCameraChangeListener became deprecated. I've also faced with this problem and I discovered that onCameraIdleListener does the similar trick. As I see so far it's callback method onCameraIdle
called always after onMapReady
. So my approach looks like this piece of code (considering it put in Activity
) for Google Maps SDK 9.6+:
@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
*/
}