MapView Gestures detector

This example shows Gestures detectors on MapView

  • Gestures detectors on MapView

  • Enable/Disable Gesture detector on Mapview

MapView Gestures detector

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

1
package ai.nextbillion;
2
3
import android.graphics.Typeface;
4
import android.os.Bundle;
5
import android.os.Handler;
6
import android.view.LayoutInflater;
7
import android.view.Menu;
8
import android.view.MenuItem;
9
import android.view.View;
10
import android.view.ViewGroup;
11
import android.widget.RelativeLayout;
12
import android.widget.TextView;
13
14
import java.lang.annotation.Retention;
15
import java.util.ArrayList;
16
import java.util.List;
17
18
import ai.nextbillion.gestures.AndroidGesturesManager;
19
import ai.nextbillion.gestures.MoveGestureDetector;
20
import ai.nextbillion.gestures.RotateGestureDetector;
21
import ai.nextbillion.gestures.ShoveGestureDetector;
22
import ai.nextbillion.gestures.StandardScaleGestureDetector;
23
import ai.nextbillion.maps.annotations.Marker;
24
import ai.nextbillion.maps.annotations.MarkerOptions;
25
import ai.nextbillion.maps.camera.CameraPosition;
26
import ai.nextbillion.maps.camera.CameraUpdateFactory;
27
import ai.nextbillion.maps.core.MapView;
28
import ai.nextbillion.maps.core.NextbillionMap;
29
import ai.nextbillion.maps.core.UiSettings;
30
import ai.nextbillion.maps.geometry.LatLng;
31
import ai.nextbillion.utils.FontCache;
32
import ai.nextbillion.utils.ResourceUtils;
33
import androidx.annotation.ColorInt;
34
import androidx.annotation.IntDef;
35
import androidx.annotation.NonNull;
36
import androidx.annotation.Nullable;
37
import androidx.appcompat.app.AppCompatActivity;
38
import androidx.core.content.ContextCompat;
39
import androidx.recyclerview.widget.LinearLayoutManager;
40
import androidx.recyclerview.widget.RecyclerView;
41
42
import static java.lang.annotation.RetentionPolicy.SOURCE;
43
44
/**
45
* Test activity showcasing APIs around gestures implementation.
46
*/
47
public 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.

© 2024 NextBillion.ai all rights reserved.