MapView Gestures detector
This example shows Gestures detectors on MapView
-
Gestures detectors on MapView
-
Enable/Disable Gesture detector on Mapview

For all code examples, refer to Android Maps SDK Code Examples
activity_gesture_detector.xml view source
1<?xml version="1.0" encoding="utf-8"?>
2<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 xmlns:app="http://schemas.android.com/apk/res-auto"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent">
7
8 <ai.nextbillion.maps.core.MapView
9 android:id="@id/mapView"
10 android:layout_width="match_parent"
11 android:layout_height="match_parent"
12 app:nbmap_localIdeographEnabled="false"
13 app:nbmap_cameraTargetLat="51.50325"
14 app:nbmap_cameraTargetLng="-0.11968"
15 app:nbmap_cameraZoom="15" />
16
17 <androidx.recyclerview.widget.RecyclerView
18 android:id="@+id/alerts_recycler"
19 android:layout_width="wrap_content"
20 android:layout_height="wrap_content"
21 android:layout_alignParentLeft="true"
22 android:layout_alignParentStart="true"
23 android:layout_alignParentTop="true"
24 android:background="#97cdcfd3" />
25
26</RelativeLayout>
GestureDetectorActivity view source
1package ai.nextbillion;
2
3import android.graphics.Typeface;
4import android.os.Bundle;
5import android.os.Handler;
6import android.view.LayoutInflater;
7import android.view.Menu;
8import android.view.MenuItem;
9import android.view.View;
10import android.view.ViewGroup;
11import android.widget.RelativeLayout;
12import android.widget.TextView;
13
14import java.lang.annotation.Retention;
15import java.util.ArrayList;
16import java.util.List;
17
18import ai.nextbillion.gestures.AndroidGesturesManager;
19import ai.nextbillion.gestures.MoveGestureDetector;
20import ai.nextbillion.gestures.RotateGestureDetector;
21import ai.nextbillion.gestures.ShoveGestureDetector;
22import ai.nextbillion.gestures.StandardScaleGestureDetector;
23import ai.nextbillion.maps.annotations.Marker;
24import ai.nextbillion.maps.annotations.MarkerOptions;
25import ai.nextbillion.maps.camera.CameraPosition;
26import ai.nextbillion.maps.camera.CameraUpdateFactory;
27import ai.nextbillion.maps.core.MapView;
28import ai.nextbillion.maps.core.NextbillionMap;
29import ai.nextbillion.maps.core.UiSettings;
30import ai.nextbillion.maps.geometry.LatLng;
31import ai.nextbillion.utils.FontCache;
32import ai.nextbillion.utils.ResourceUtils;
33import androidx.annotation.ColorInt;
34import androidx.annotation.IntDef;
35import androidx.annotation.NonNull;
36import androidx.annotation.Nullable;
37import androidx.appcompat.app.AppCompatActivity;
38import androidx.core.content.ContextCompat;
39import androidx.recyclerview.widget.LinearLayoutManager;
40import androidx.recyclerview.widget.RecyclerView;
41
42import static java.lang.annotation.RetentionPolicy.SOURCE;
43
44/**
45 * Test activity showcasing APIs around gestures implementation.
46 */
47public class GestureDetectorActivity extends AppCompatActivity {
48
49 private static final int MAX_NUMBER_OF_ALERTS = 30;
50
51 private MapView mapView;
52 private NextbillionMap nextbillionMap;
53 private RecyclerView recyclerView;
54 private GestureAlertsAdapter gestureAlertsAdapter;
55
56 private AndroidGesturesManager gesturesManager;
57
58 @Nullable
59 private Marker marker;
60 @Nullable
61 private LatLng focalPointLatLng;
62
63 @Override
64 protected void onCreate(Bundle savedInstanceState) {
65 super.onCreate(savedInstanceState);
66 setContentView(R.layout.activity_gesture_detector);
67
68 mapView = findViewById(R.id.mapView);
69 mapView.onCreate(savedInstanceState);
70 mapView.getMapAsync(nextbillionMap -> {
71 GestureDetectorActivity.this.nextbillionMap = nextbillionMap;
72 nextbillionMap.setStyle(StyleConstants.NBMAP_STREETS);
73 CameraPosition cameraPosition = new CameraPosition.Builder()
74 .target(new LatLng(22.70418008712976, 78.66264025041812))
75 .zoom(6)
76 .tilt(30)
77 .tilt(0)
78 .build();
79 nextbillionMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
80 initializeMap();
81 });
82
83 recyclerView = findViewById(R.id.alerts_recycler);
84 recyclerView.setLayoutManager(new LinearLayoutManager(this));
85
86 gestureAlertsAdapter = new GestureAlertsAdapter();
87 recyclerView.setAdapter(gestureAlertsAdapter);
88 }
89
90 @Override
91 protected void onResume() {
92 super.onResume();
93 mapView.onResume();
94 }
95
96 @Override
97 protected void onPause() {
98 super.onPause();
99 gestureAlertsAdapter.cancelUpdates();
100 mapView.onPause();
101 }
102
103 @Override
104 protected void onStart() {
105 super.onStart();
106 mapView.onStart();
107 }
108
109 @Override
110 protected void onStop() {
111 super.onStop();
112 mapView.onStop();
113 }
114
115 @Override
116 public void onLowMemory() {
117 super.onLowMemory();
118 mapView.onLowMemory();
119 }
120
121 @Override
122 protected void onDestroy() {
123 super.onDestroy();
124 mapView.onDestroy();
125 }
126
127 @Override
128 protected void onSaveInstanceState(Bundle outState) {
129 super.onSaveInstanceState(outState);
130 mapView.onSaveInstanceState(outState);
131 }
132
133 private void initializeMap() {
134 gesturesManager = nextbillionMap.getGesturesManager();
135
136 RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
137 layoutParams.height = (int) (mapView.getHeight() / 1.75);
138 layoutParams.width = (mapView.getWidth() / 3);
139 recyclerView.setLayoutParams(layoutParams);
140
141 attachListeners();
142
143 fixedFocalPointEnabled(nextbillionMap.getUiSettings().getFocalPoint() != null);
144 }
145
146 public void attachListeners() {
147 nextbillionMap.addOnMoveListener(new NextbillionMap.OnMoveListener() {
148 @Override
149 public void onMoveBegin(@NonNull MoveGestureDetector detector) {
150 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "MOVE START"));
151 }
152
153 @Override
154 public void onMove(@NonNull MoveGestureDetector detector) {
155 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "MOVE PROGRESS"));
156 }
157
158 @Override
159 public void onMoveEnd(@NonNull MoveGestureDetector detector) {
160 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "MOVE END"));
161 recalculateFocalPoint();
162 }
163 });
164
165 nextbillionMap.addOnRotateListener(new NextbillionMap.OnRotateListener() {
166 @Override
167 public void onRotateBegin(@NonNull RotateGestureDetector detector) {
168 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "ROTATE START"));
169 }
170
171 @Override
172 public void onRotate(@NonNull RotateGestureDetector detector) {
173 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "ROTATE PROGRESS"));
174 recalculateFocalPoint();
175 }
176
177 @Override
178 public void onRotateEnd(@NonNull RotateGestureDetector detector) {
179 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "ROTATE END"));
180 }
181 });
182
183 nextbillionMap.addOnScaleListener(new NextbillionMap.OnScaleListener() {
184 @Override
185 public void onScaleBegin(@NonNull StandardScaleGestureDetector detector) {
186 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "SCALE START"));
187 if (focalPointLatLng != null) {
188 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "INCREASING MOVE THRESHOLD"));
189 gesturesManager.getMoveGestureDetector().setMoveThreshold(
190 ResourceUtils.convertDpToPx(GestureDetectorActivity.this, 175));
191
192 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "MANUALLY INTERRUPTING MOVE"));
193 gesturesManager.getMoveGestureDetector().interrupt();
194 }
195 recalculateFocalPoint();
196 }
197
198 @Override
199 public void onScale(@NonNull StandardScaleGestureDetector detector) {
200 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "SCALE PROGRESS"));
201 }
202
203 @Override
204 public void onScaleEnd(@NonNull StandardScaleGestureDetector detector) {
205 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "SCALE END"));
206
207 if (focalPointLatLng != null) {
208 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "REVERTING MOVE THRESHOLD"));
209 gesturesManager.getMoveGestureDetector().setMoveThreshold(0f);
210 }
211 }
212 });
213
214 nextbillionMap.addOnShoveListener(new NextbillionMap.OnShoveListener() {
215 @Override
216 public void onShoveBegin(@NonNull ShoveGestureDetector detector) {
217 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "SHOVE START"));
218 }
219
220 @Override
221 public void onShove(@NonNull ShoveGestureDetector detector) {
222 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "SHOVE PROGRESS"));
223 }
224
225 @Override
226 public void onShoveEnd(@NonNull ShoveGestureDetector detector) {
227 gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "SHOVE END"));
228 }
229 });
230 }
231
232 @Override
233 public boolean onCreateOptionsMenu(Menu menu) {
234 getMenuInflater().inflate(R.menu.menu_gestures, menu);
235 return true;
236 }
237
238 @Override
239 public boolean onOptionsItemSelected(MenuItem item) {
240 UiSettings uiSettings = nextbillionMap.getUiSettings();
241 switch (item.getItemId()) {
242 case R.id.menu_gesture_focus_point:
243 fixedFocalPointEnabled(focalPointLatLng == null);
244 return true;
245 case R.id.menu_gesture_animation:
246 uiSettings.setScaleVelocityAnimationEnabled(!uiSettings.isScaleVelocityAnimationEnabled());
247 uiSettings.setRotateVelocityAnimationEnabled(!uiSettings.isRotateVelocityAnimationEnabled());
248 uiSettings.setFlingVelocityAnimationEnabled(!uiSettings.isFlingVelocityAnimationEnabled());
249 return true;
250 case R.id.menu_gesture_rotate:
251 uiSettings.setRotateGesturesEnabled(!uiSettings.isRotateGesturesEnabled());
252 return true;
253 case R.id.menu_gesture_tilt:
254 uiSettings.setTiltGesturesEnabled(!uiSettings.isTiltGesturesEnabled());
255 return true;
256 case R.id.menu_gesture_zoom:
257 uiSettings.setZoomGesturesEnabled(!uiSettings.isZoomGesturesEnabled());
258 return true;
259 case R.id.menu_gesture_scroll:
260 uiSettings.setScrollGesturesEnabled(!uiSettings.isScrollGesturesEnabled());
261 return true;
262 case R.id.menu_gesture_double_tap:
263 uiSettings.setDoubleTapGesturesEnabled(!uiSettings.isDoubleTapGesturesEnabled());
264 return true;
265 case R.id.menu_gesture_quick_zoom:
266 uiSettings.setQuickZoomGesturesEnabled(!uiSettings.isQuickZoomGesturesEnabled());
267 return true;
268 case R.id.menu_gesture_scroll_horizontal:
269 uiSettings.setHorizontalScrollGesturesEnabled(!uiSettings.isHorizontalScrollGesturesEnabled());
270 return true;
271 }
272 return super.onOptionsItemSelected(item);
273 }
274
275 private void fixedFocalPointEnabled(boolean enabled) {
276 if (enabled) {
277 focalPointLatLng = new LatLng(51.50325, -0.12968);
278 marker = nextbillionMap.addMarker(new MarkerOptions().position(focalPointLatLng));
279 nextbillionMap.easeCamera(CameraUpdateFactory.newLatLngZoom(focalPointLatLng, 16),
280 new NextbillionMap.CancelableCallback() {
281 @Override
282 public void onCancel() {
283 recalculateFocalPoint();
284 }
285
286 @Override
287 public void onFinish() {
288 recalculateFocalPoint();
289 }
290 });
291 } else {
292 if (marker != null) {
293 nextbillionMap.removeMarker(marker);
294 marker = null;
295 }
296 focalPointLatLng = null;
297 nextbillionMap.getUiSettings().setFocalPoint(null);
298 }
299 }
300
301 private void recalculateFocalPoint() {
302 if (focalPointLatLng != null) {
303 nextbillionMap.getUiSettings().setFocalPoint(
304 nextbillionMap.getProjection().toScreenLocation(focalPointLatLng)
305 );
306 }
307 }
308
309 private static class GestureAlertsAdapter extends RecyclerView.Adapter<GestureAlertsAdapter.ViewHolder> {
310
311 private boolean isUpdating;
312 private final Handler updateHandler = new Handler();
313 private final List<GestureAlert> alerts = new ArrayList<>();
314
315 public static class ViewHolder extends RecyclerView.ViewHolder {
316
317 TextView alertMessageTv;
318
319 @ColorInt
320 public int textColor;
321
322 ViewHolder(View view) {
323 super(view);
324 Typeface typeface = FontCache.get("Roboto-Regular.ttf", view.getContext());
325 alertMessageTv = view.findViewById(R.id.alert_message);
326 alertMessageTv.setTypeface(typeface);
327 }
328 }
329
330
331 @NonNull
332 @Override
333 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
334 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gesture_alert, parent, false);
335 return new ViewHolder(view);
336 }
337
338 @Override
339 public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
340 GestureAlert alert = alerts.get(position);
341 holder.alertMessageTv.setText(alert.getMessage());
342 holder.alertMessageTv.setTextColor(
343 ContextCompat.getColor(holder.alertMessageTv.getContext(), alert.getColor()));
344 }
345
346 @Override
347 public int getItemCount() {
348 return alerts.size();
349 }
350
351 void addAlert(GestureAlert alert) {
352 for (GestureAlert gestureAlert : alerts) {
353 if (gestureAlert.getAlertType() != GestureAlert.TYPE_PROGRESS) {
354 break;
355 }
356
357 if (alert.getAlertType() == GestureAlert.TYPE_PROGRESS && gestureAlert.equals(alert)) {
358 return;
359 }
360 }
361
362 if (getItemCount() >= MAX_NUMBER_OF_ALERTS) {
363 alerts.remove(getItemCount() - 1);
364 }
365
366 alerts.add(0, alert);
367 if (!isUpdating) {
368 isUpdating = true;
369 updateHandler.postDelayed(updateRunnable, 250);
370 }
371 }
372
373 private Runnable updateRunnable = new Runnable() {
374 @Override
375 public void run() {
376 notifyDataSetChanged();
377 isUpdating = false;
378 }
379 };
380
381 void cancelUpdates() {
382 updateHandler.removeCallbacksAndMessages(null);
383 }
384 }
385
386 private static class GestureAlert {
387 @Retention(SOURCE)
388 @IntDef( {TYPE_NONE, TYPE_START, TYPE_PROGRESS, TYPE_END, TYPE_OTHER})
389 @interface Type {
390 }
391
392 static final int TYPE_NONE = 0;
393 static final int TYPE_START = 1;
394 static final int TYPE_END = 2;
395 static final int TYPE_PROGRESS = 3;
396 static final int TYPE_OTHER = 4;
397
398 @Type
399 private int alertType;
400
401 private String message;
402
403 @ColorInt
404 private int color;
405
406 GestureAlert(@Type int alertType, String message) {
407 this.alertType = alertType;
408 this.message = message;
409
410 switch (alertType) {
411 case TYPE_NONE:
412 color = android.R.color.black;
413 break;
414 case TYPE_END:
415 color = android.R.color.holo_red_dark;
416 break;
417 case TYPE_OTHER:
418 color = android.R.color.holo_purple;
419 break;
420 case TYPE_PROGRESS:
421 color = android.R.color.holo_orange_dark;
422 break;
423 case TYPE_START:
424 color = android.R.color.holo_green_dark;
425 break;
426 }
427 }
428
429 int getAlertType() {
430 return alertType;
431 }
432
433 String getMessage() {
434 return message;
435 }
436
437 int getColor() {
438 return color;
439 }
440
441 @Override
442 public boolean equals(Object o) {
443 if (this == o) {
444 return true;
445 }
446 if (o == null || getClass() != o.getClass()) {
447 return false;
448 }
449
450 GestureAlert that = (GestureAlert) o;
451
452 if (alertType != that.alertType) {
453 return false;
454 }
455 return message != null ? message.equals(that.message) : that.message == null;
456 }
457
458 @Override
459 public int hashCode() {
460 int result = alertType;
461 result = 31 * result + (message != null ? message.hashCode() : 0);
462 return result;
463 }
464 }
465}
The code provided is an Android activity called GestureDetectorActivity. It showcases various APIs related to gesture implementation. Here is a summary of the code:
Initialization of MapView:
-
The MapView is instantiated and initialized in the onCreate() method using findViewById().
-
The map is asynchronously loaded using getMapAsync(), and the initialization of the map is done in the callback.
-
The map style is set to "NBMAP_STREETS" using the setStyle() method.
-
The camera position is set to a specific location with a target LatLng, zoom level and tilt using the CameraPosition.Builder and moveCamera() methods.
Gesture Detectors on MapView:
-
setOnTouchListener(View.OnTouchListener listener): Sets an OnTouchListener to receive touch events for the map view.
-
setOnMapClickListener(MapView.OnMapClickListener listener): Sets a listener to be invoked when the map is clicked.
-
setOnMapLongClickListener(MapView.OnMapLongClickListener listener): Sets a listener to be invoked when the map is long-clicked.
-
setOnMapDoubleClickListener(MapView.OnMapDoubleClickListener listener): Sets a listener to be invoked when the map is double-clicked.
-
setOnMapScaleListener(MapView.OnMapScaleListener listener): Sets a listener to be invoked when the map scale changes.
-
setOnMapScrollListener(MapView.OnMapScrollListener listener): Sets a listener to be invoked when the map is scrolled.
-
setOnMapFlingListener(MapView.OnMapFlingListener listener): Sets a listener to be invoked when the map is flung.
-
setOnMapRotateListener(MapView.OnMapRotateListener listener): Sets a listener to be invoked when the map is rotated.
-
setOnMapTranslateListener(MapView.OnMapTranslateListener listener): Sets a listener to be invoked when the map is translated.
-
setOnMapPinchListener(MapView.OnMapPinchListener listener): Sets a listener to be invoked when the map is pinched.
-
setOnMapSingleFingerTapListener(MapView.OnMapSingleFingerTapListener listener): Sets a listener to be invoked when the map is single-finger tapped.
-
setOnMapTwoFingerTapListener(MapView.OnMapTwoFingerTapListener listener): Sets a listener to be invoked when the map is two-finger tapped.
Enable/Disable Gesture Detectors on MapView:
-
setRotateGesturesEnabled(boolean enabled): Enables or disables rotate gestures on the map.
-
isRotateGesturesEnabled(): Returns whether rotate gestures are enabled or not.
-
setTiltGesturesEnabled(boolean enabled): Enables or disables tilt gestures on the map.
-
isTiltGesturesEnabled(): Returns whether tilt gestures are enabled or not.
-
setZoomGesturesEnabled(boolean enabled): Enables or disables zoom gestures on the map.
-
isZoomGesturesEnabled(): Returns whether zoom gestures are enabled or not.
-
setScrollGesturesEnabled(boolean enabled): Enables or disables scroll gestures on the map.
-
isScrollGesturesEnabled(): Returns whether scroll gestures are enabled or not.
-
setDoubleTapGesturesEnabled(boolean enabled): Enables or disables double-tap gestures on the map.
-
isDoubleTapGesturesEnabled(): Returns whether double tap gestures are enabled or not.
-
setQuickZoomGesturesEnabled(boolean enabled): Enables or disables quick zoom gestures on the map.
-
isQuickZoomGesturesEnabled(): Returns whether quick zoom gestures are enabled or not.
-
setHorizontalScrollGesturesEnabled(boolean enabled): Enables or disables horizontal scroll gestures on the map.
-
isHorizontalScrollGesturesEnabled(): Returns whether horizontal scroll gestures are enabled or not.
-
setFlingVelocityAnimationEnabled(boolean enabled): Enables or disables fling velocity animation.
-
isFlingVelocityAnimationEnabled(): Returns whether fling velocity animation is enabled or not.
-
setScaleVelocityAnimationEnabled(boolean enabled): Enables or disables scale velocity animation.
-
isScaleVelocityAnimationEnabled(): Returns whether scale velocity animation is enabled or not.
-
setRotateVelocityAnimationEnabled(boolean enabled): Enables or disables rotate velocity animation.
-
isRotateVelocityAnimationEnabled(): Returns, whether rotate velocity animation, is enabled or not.
-
setFocalPoint(PointF point): Sets the focal point on the map for gestures.
-
getFocalPoint(): Returns the current focal point on the map for gestures.
Overall, the code demonstrates how to implement and manage gesture detectors on a MapView in an Android application.