I'm looking to do something similar to the way Uber handles pinch zoom events. No matter where you pinch on the screen, it keeps the map centered and zooms in on the center location. Is there a way to do this without having some sort of overlay over the map fragment? Or should I just disable maps' events, create an overlay over the map fragment, and handle all zoom / other events from the overlay?
In the bottom right of the map, you should see a button that looks like a target. Tap that, and it centers the map over you and tracks your movements.
You can change the zoom level of the map using simple steps. Step 1 Go to Add or Edit Map page . Step 2 Select 'Default zoom level' in the 'Map Information section'. Step 3 click save map and see the changes.
To fix Google maps zooming problems, for Google maps default zoom you want to know how to change the zoom level on Google Maps. You can change the zoom level by going to the Edit map page and then selecting 'default zoom level' in the map information section and then clicking save map.
Zoom in the mapDouble tap a spot on the map, and then: Drag down to zoom in. Drag up to zoom out.
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).
Here's the code for what MechEthan is thinking of.
First you have to detect double-tap on an overlay view.
public class TouchableWrapper extends FrameLayout {
private final GestureDetector.SimpleOnGestureListener mGestureListener
= new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
//Notify the event bus (I am using Otto eventbus of course) that you have just received a double-tap event on the map, inside the event bus event listener
EventBus_Singleton.getInstance().post(new EventBus_Poster("double_tapped_map"));
return true;
}
};
public TouchableWrapper(Context context) {
super(context);
mGestureDetector = new GestureDetectorCompat(context, mGestureListener);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mGestureDetector.onTouchEvent(ev);
return super.onInterceptTouchEvent(ev);
}
}
Wherever it is that you are grabbing your mapView, wrap that mapView inside the TouchableWrapper created above. This is how I do it because I have the issue of needing to add a mapFragment into another fragment so I need a custom SupportMapFragment to do this
public class CustomMap_Fragment extends SupportMapFragment {
TouchableWrapper mTouchView;
public CustomMap_Fragment() {
super();
}
public static CustomMap_Fragment newInstance() {
return new CustomMap_Fragment();
}
@Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
View mapView = super.onCreateView(arg0, arg1, arg2);
Fragment fragment = getParentFragment();
if (fragment != null && fragment instanceof OnMapReadyListener) {
((OnMapReadyListener) fragment).onMapReady();
}
mTouchView = new TouchableWrapper(getActivity());
mTouchView.addView(mapView);
return mTouchView;
}
public static interface OnMapReadyListener {
void onMapReady();
}
}
Inside my Map_Fragment (which in the end will sit inside a FrameLayout in an activity that supports navigation drawer and fragment transactions for switching the views)
mMapFragment = CustomMap_Fragment.newInstance();
getChildFragmentManager().beginTransaction().replace(R.id.map_container, mMapFragment).commit();
Now finally inside this same Fragment where I just got my map, the EventBus receiver will do the following action when it receives "double_tapped_map":
@Subscribe public void eventBus_ListenerMethod(AnswerAvailableEvent event) {
//Construct a CameraUpdate object that will zoom into the exact middle of the map, with a zoom of currentCameraZoom + 1 unit
zoomInUpdate = CameraUpdateFactory.zoomIn();
//Run that with a speed of 400 ms.
map.animateCamera(zoomInUpdate, 400, null);
}
Note: To achieve this perfectly you disable zoomGestures on your map (meaning you do myMap.getUiSettings().setZoomGesturesEnabled(false);
. If you don't do that, you will be able to double-tap very quickly on the map and you will see that it will zoom away from the center because the implementation of double tap is exactly as I had in the first answer I posted, which is that they subtract current time from previous tap-time, so in that window you can slip in a third tap and it will not trigger the event bus event and google map will catch it instead; So disable Zoom gestures.
But then, you will see that pinch-in/out will not work anymore and you have to handle pinch also, which I've also done but needs like 1 more hour and I havent gotten the time to do that yet but 100% I will update the answer when I do that.
TIP: Uber has disabled rotate gestures on the map also. map.getUiSettings().setRotateGesturesEnabled(false);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With