On Zoom Event for Google Maps on Android

On zoom event for google maps on android

With the Google Maps Android API v2 you can use a GoogleMap.OnCameraChangeListener like this:

mMap.setOnCameraChangeListener(new OnCameraChangeListener() {

private float currentZoom = -1;

@Override
public void onCameraChange(CameraPosition pos) {
if (pos.zoom != currentZoom){
currentZoom = pos.zoom;
// do you action here
}
}
});

zoom level listener in google maps v2 in android

Create an implementation of OnCameraChangeListener, and pass an instance of it to setOnCameraChangeListener() of your GoogleMap. Your listener should be called with onCameraChange() whenever the user changes the zoom, center, or tilt. You find out the new zoom level from the CameraPosition object that you are passed.

Android Google Map show markers on zoom change

Okay so you can use a CameraChangeListener:

private HashMap<Integer, Marker> courseMarkers = new HashMap<Integer, Marker>();
//all your visible markers
ArrayList<Item> yourMarkerList = new ArrayList<Item>();
//method to add all your markers with unique ids
addItemsToMap(); //first call to initially show the markers you want

googleMap.setOnCameraChangeListener(new OnCameraChangeListener() {

private float currentZoom = -1; //keep track of your current zoom level

@Override
public void onCameraChange(CameraPosition camera) {
if (camera.zoom != currentZoom){
currentZoom = camera.zoom;
//here you will then check your markers
addItemsToMap(yourMarkerList);
}
}
});

You will need a class Item that has a variable for a unique int id and a MarkerOptions

private void addItemsToMap(List<Item> items)
{
if(this.mMap != null)
{
//This is the current user-viewable region of the map
LatLngBounds bounds = this.mMap.getProjection().getVisibleRegion().latLngBounds;

//Loop through all the items that are available to be placed on the map
for(Item m : item)
{

//If the item is within the the bounds of the screen
if(bounds.contains(item.getMarker().getPosition()))
{
//If the item isn't already being displayed
if(!courseMarkers.containsKey(item.getId()))
{
//Add the Marker to the Map and keep track of it with the HashMap
//getMarkerForItem just returns a MarkerOptions object
this.courseMarkers.put(item.getId(), this.googleMap.addMarker(item.getMarker())); //getmarkerforitem
}
}

//If the marker is off screen
else
{
//If the course was previously on screen
if(courseMarkers.containsKey(item.getId()))
{
//1. Remove the Marker from the GoogleMap
courseMarkers.get(item.getId()).remove();

//2. Remove the reference to the Marker from the HashMap
courseMarkers.remove(item.getId());
}
}
}
}

}

This should do it

How to listen to Android GoogleMap pan and zoom

Take a look a setOnCameraChangeListener

setOnCameraChangeListener

request a new google map on zoom event

As @DeividiCavarzan commented, Google Map already does this.

The heavily pixeled view you're seeing is a "preview" of the area that is not loaded yet.

It it basically the stretch previous image that was there, but since you're zooming, it's invalidated and a download is made to get a more refine image.

If your network connection is bad, then this pixeled image will show for a while, yes. But nothing can be done to avoid that: you need offline map or connectivity.

Zoom Move Pinch listener in google maps v2 in android

automatically the following event: "setOnCameraChangeListener" is called.

Nothing is done automatically.

updated every few seconds to show some new points on the map.

and:

Then readjust the Zoom to fit the actual Marker on the map.

This seems like a very bad user experience: forcing user to look at your marker when they just want to swipe to Africa.

Anyway, if you have GoogleMap.animateCamera call in your code, you may use CancelableCallback parameter to help you distinguish between user activity and code call resulting in onCameraChange. See this comment on gmaps-api-issues for how to achieve this.

Keep map centered regardless of where you pinch zoom on android

I've founded complete solution after spending about 3 days to search on google. My answer is edited from https://stackoverflow.com/a/32734436/3693334.

public class CustomMapView extends MapView {

private int fingers = 0;
private GoogleMap googleMap;
private long lastZoomTime = 0;
private float lastSpan = -1;
private Handler handler = new Handler();

private ScaleGestureDetector scaleGestureDetector;
private GestureDetector gestureDetector;

public CustomMapView(Context context) {
super(context);
}

public CustomMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public CustomMapView(Context context, AttributeSet attrs, int style) {
super(context, attrs, style);
}

public CustomMapView(Context context, GoogleMapOptions options) {
super(context, options);
}

public void init(GoogleMap map) {
scaleGestureDetector = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (lastSpan == -1) {
lastSpan = detector.getCurrentSpan();
} else if (detector.getEventTime() - lastZoomTime >= 50) {
lastZoomTime = detector.getEventTime();
googleMap.animateCamera(CameraUpdateFactory.zoomBy(getZoomValue(detector.getCurrentSpan(), lastSpan)), 50, null);
lastSpan = detector.getCurrentSpan();
}
return false;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
lastSpan = -1;
return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {
lastSpan = -1;

}
});
gestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTapEvent(MotionEvent e) {

disableScrolling();
googleMap.animateCamera(CameraUpdateFactory.zoomIn(), 400, null);

return true;
}
});
googleMap = map;
}

private float getZoomValue(float currentSpan, float lastSpan) {
double value = (Math.log(currentSpan / lastSpan) / Math.log(1.55d));
return (float) value;
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
fingers = fingers + 1;
break;
case MotionEvent.ACTION_POINTER_UP:
fingers = fingers - 1;
break;
case MotionEvent.ACTION_UP:
fingers = 0;
break;
case MotionEvent.ACTION_DOWN:
fingers = 1;
break;
}
if (fingers > 1) {
disableScrolling();
} else if (fingers < 1) {
enableScrolling();
}
if (fingers > 1) {
return scaleGestureDetector.onTouchEvent(ev);
} else {
return super.dispatchTouchEvent(ev);
}
}

private void enableScrolling() {
if (googleMap != null && !googleMap.getUiSettings().isScrollGesturesEnabled()) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
googleMap.getUiSettings().setAllGesturesEnabled(true);
}
}, 50);
}
}

private void disableScrolling() {
handler.removeCallbacksAndMessages(null);
if (googleMap != null && googleMap.getUiSettings().isScrollGesturesEnabled()) {
googleMap.getUiSettings().setAllGesturesEnabled(false);
}
}
}

and customize MapFragment

public class CustomMapFragment extends Fragment {

CustomMapView view;
Bundle bundle;
GoogleMap map;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bundle = savedInstanceState;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map, container, false);

view = (CustomMapView) v.findViewById(R.id.mapView);
view.onCreate(bundle);
view.onResume();

map = view.getMap();
view.init(map);

MapsInitializer.initialize(getActivity());

return v;
}

public GoogleMap getMap() {
return map;
}

@Override
public void onResume() {
super.onResume();
view.onResume();
}

@Override
public void onPause() {
super.onPause();
view.onPause();
}

@Override
public void onDestroy() {
super.onDestroy();
view.onDestroy();
}

@Override
public void onLowMemory() {
super.onLowMemory();
view.onLowMemory();
}
}

Finally, in your activity:

....
<fragment
android:id="@+id/map"
class="yourpackage.CustomMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
...

I've already tested on Android 4.1 (API 16) and latter, it work fine and smooth. (About API < 16, I haven't any device to test).

Centered zooming a Google Map through a wrapper?

So this is how i managed to do this.

I created two classes for detecting certain events, like a fling, double tap, scroll(drag) and scale(zoom):

public class PinchListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

private GoogleMap mGoogleMap;

public PinchListener(GoogleMap googleMap) {
this.mGoogleMap = googleMap;
}

@Override
public boolean onScale(ScaleGestureDetector detector) {
double zoom = mGoogleMap.getCameraPosition().zoom;
zoom = zoom + Math.log(detector.getScaleFactor()) / Math.log(1.5d);
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(mGoogleMap.getCameraPosition().target, (float) zoom);
mGoogleMap.moveCamera(update);
return true;
}
}

public class GestureListener extends GestureDetector.SimpleOnGestureListener {

private GoogleMap mGoogleMap;
private boolean mIsInAnimation;

public GestureListener(GoogleMap googleMap) {
this.mGoogleMap = googleMap;
mIsInAnimation = false;
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
LatLng target = mGoogleMap.getCameraPosition().target;
Point screenPoint = mGoogleMap.getProjection().toScreenLocation(target);
Point newPoint = new Point(screenPoint.x + (int) distanceX, screenPoint.y + (int) distanceY);
LatLng mapNewTarget = mGoogleMap.getProjection().fromScreenLocation(newPoint);

CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget, mGoogleMap.getCameraPosition().zoom);
mGoogleMap.moveCamera(update);
return true;
}

@Override
public boolean onDoubleTap(MotionEvent e) {
mGoogleMap.animateCamera(CameraUpdateFactory.zoomIn(), 400, null);
return true;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mIsInAnimation) return false;
int velocity = (int) Math.sqrt(velocityX * velocityX + velocityY * velocityY);
if (velocity < 500) return false;
double k1 = 0.002d; /*exipemental*/
double k2 = 0.002d;/*exipemental*/

LatLng target = mGoogleMap.getCameraPosition().target;
Point screenPoint = mGoogleMap.getProjection().toScreenLocation(target);

Point newPoint = new Point(screenPoint.x - (int) (velocityX * k1),
screenPoint.y - (int) (velocityY * k1));
LatLng mapNewTarget = mGoogleMap.getProjection().fromScreenLocation(newPoint);

CameraUpdate update = CameraUpdateFactory.newLatLngZoom(
mapNewTarget, mGoogleMap.getCameraPosition().zoom);

tryUpdateCamera(update, (int) (velocity * k2));
return true;
}

private void tryUpdateCamera(CameraUpdate update, int animateTime) {
mIsInAnimation = true;
mGoogleMap.animateCamera(update, animateTime, new GoogleMap.CancelableCallback() {
@Override
public void onFinish() {
mIsInAnimation = false;
}

@Override
public void onCancel() {
mIsInAnimation = false;
}
});
}
}

I then initialized a GestureDetector and ScaleGestureDetector in my MainActivity:

ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(this, new PinchListener(mMap));
GestureDetector mGestureDetector = new GestureDetector(this, new GestureListener(mMap));

I had already created a transparent image wrapper in my activity_main to detect all these events and then simulate them on the Google Map below, like this:

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map"/>

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mapWrapper"
/>

And then finally in my MainActivity i created an OnTouchListener for the wrapper:

mMapWrapper = findViewById(R.id.mapWrapper);

mMapWrapper.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mMode = Mode.DRAG;
mGestureDetector.onTouchEvent(event);
mScaleGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_POINTER_DOWN:
mMode = Mode.ZOOM;
mScaleGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mMode = Mode.NONE;
mGestureDetector.onTouchEvent(event);
break;
case MotionEvent.ACTION_MOVE:
if (mMode == Mode.DRAG) {
mGestureDetector.onTouchEvent(event);
} else if (mMode == Mode.ZOOM) {
mScaleGestureDetector.onTouchEvent(event);
}
break;
}

return true;
}
});

In the last step i "feed" the right detector the events that i want. If i had not done that, the zooming would not be completely centered since sometimes onScroll(Drag) events would be called and that would move the map.

In google map using pinch to zoom in and zoom out with the current location as centre of the map

Create TouchableWrapper and use it in MapFragment

public class TouchableWrapper extends FrameLayout {

private GoogleMap mGoogleMap = null;

public TouchableWrapper(Context context) {
super(context);
}

public void setGoogleMap(GoogleMap googleMap) {
mGoogleMap = googleMap;
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);

switch (event.getAction() & MotionEvent.ACTION_MASK) {

case MotionEvent.ACTION_DOWN:
mGoogleMap.getUiSettings().setScrollGesturesEnabled(true);

long thisTime = System.currentTimeMillis();
if (thisTime - mLastTouchTime < ViewConfiguration.getDoubleTapTimeout()) {

if (mGoogleMap != null) {
LatLng zoomCenter = mGoogleMap.getProjection().fromScreenLocation(new Point((int) event.getX(), (int) event.getY()));
float currentZoom = mGoogleMap.getCameraPosition().zoom;

int mapViewHeight = getHeight();
int mapViewWidth = getWidth();

Projection projection = mGoogleMap.getProjection();

geographicalPosition = projection.fromScreenLocation(new Point(mapViewWidth / 2, mapViewHeight / 2));

mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(geographicalPosition.latitude, geographicalPosition.longitude), currentZoom + 1));

}
mLastTouchTime = -1;
} else {
mLastTouchTime = thisTime;
mGoogleMap.getUiSettings().setZoomGesturesEnabled(false);
}
break;

case MotionEvent.ACTION_POINTER_DOWN:
mGoogleMap.getUiSettings().setZoomGesturesEnabled(false);
mGoogleMap.getUiSettings().setScrollGesturesEnabled(false);
break;

case MotionEvent.ACTION_POINTER_UP:
mGoogleMap.getUiSettings().setScrollGesturesEnabled(true);
break;

case MotionEvent.ACTION_UP:
mGoogleMap.getUiSettings().setScrollGesturesEnabled(true);
break;
}

return super.dispatchTouchEvent(event);
}

ScaleGestureDetector mScaleDetector = new ScaleGestureDetector(getContext(),
new ScaleGestureDetector.SimpleOnScaleGestureListener() {

private float scaleFactor = 1f;

@Override
public boolean onScale(ScaleGestureDetector detector) {
// store scale factor for detect zoom "direction" on end
scaleFactor = detector.getScaleFactor();
float currentZoom = mGoogleMap.getCameraPosition().zoom;
int mapViewHeight = getHeight();
int mapViewWidth = getWidth();
if (scaleFactor > 1) {
// zoom in detected
geographicalPosition = mGoogleMap.getProjection().fromScreenLocation(new Point(mapViewWidth / 2, mapViewHeight / 2));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(geographicalPosition, currentZoom + 0.05f));

} else if (scaleFactor < 1) {
// zoom out detected
geographicalPosition = mGoogleMap.getProjection().fromScreenLocation(new Point(mapViewWidth / 2, mapViewHeight / 2));
mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(geographicalPosition, currentZoom - 0.05f));
}
return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {

super.onScaleEnd(detector);
}
});

}

MapFragment for that should be customized like:

public class MultiTouchMapFragment extends MapFragment {
public View mOriginalContentView;
public TouchableWrapper mTouchView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mOriginalContentView);
return mTouchView;
}

@Override
public View getView() {
return mOriginalContentView;
}
}

in MainActivity:

public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {

private GoogleMap mGoogleMap;
private MultiTouchMapFragment mMapFragment;

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

mMapFragment = (MultiTouchMapFragment) getFragmentManager()
.findFragmentById(R.id.map_fragment);
mMapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mMapFragment.mTouchView.setGoogleMap(mGoogleMap);
}

}


Related Topics



Leave a reply



Submit