In this page

MapView Gestures detector

This example shows Gestures detectors on MapView

  1. Gestures detectors on MapView

  2. 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:

  1. The MapView is instantiated and initialized in the onCreate() method using findViewById().

  2. The map is asynchronously loaded using getMapAsync(), and the initialization of the map is done in the callback.

  3. The map style is set to "NBMAP_STREETS" using the setStyle() method.

  4. 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:

  1. setOnTouchListener(View.OnTouchListener listener): Sets an OnTouchListener to receive touch events for the map view.

  2. setOnMapClickListener(MapView.OnMapClickListener listener): Sets a listener to be invoked when the map is clicked.

  3. setOnMapLongClickListener(MapView.OnMapLongClickListener listener): Sets a listener to be invoked when the map is long-clicked.

  4. setOnMapDoubleClickListener(MapView.OnMapDoubleClickListener listener): Sets a listener to be invoked when the map is double-clicked.

  5. setOnMapScaleListener(MapView.OnMapScaleListener listener): Sets a listener to be invoked when the map scale changes.

  6. setOnMapScrollListener(MapView.OnMapScrollListener listener): Sets a listener to be invoked when the map is scrolled.

  7. setOnMapFlingListener(MapView.OnMapFlingListener listener): Sets a listener to be invoked when the map is flung.

  8. setOnMapRotateListener(MapView.OnMapRotateListener listener): Sets a listener to be invoked when the map is rotated.

  9. setOnMapTranslateListener(MapView.OnMapTranslateListener listener): Sets a listener to be invoked when the map is translated.

  10. setOnMapPinchListener(MapView.OnMapPinchListener listener): Sets a listener to be invoked when the map is pinched.

  11. setOnMapSingleFingerTapListener(MapView.OnMapSingleFingerTapListener listener): Sets a listener to be invoked when the map is single-finger tapped.

  12. setOnMapTwoFingerTapListener(MapView.OnMapTwoFingerTapListener listener): Sets a listener to be invoked when the map is two-finger tapped.

Enable/Disable Gesture Detectors on MapView:

  1. setRotateGesturesEnabled(boolean enabled): Enables or disables rotate gestures on the map.

  2. isRotateGesturesEnabled(): Returns whether rotate gestures are enabled or not.

  3. setTiltGesturesEnabled(boolean enabled): Enables or disables tilt gestures on the map.

  4. isTiltGesturesEnabled(): Returns whether tilt gestures are enabled or not.

  5. setZoomGesturesEnabled(boolean enabled): Enables or disables zoom gestures on the map.

  6. isZoomGesturesEnabled(): Returns whether zoom gestures are enabled or not.

  7. setScrollGesturesEnabled(boolean enabled): Enables or disables scroll gestures on the map.

  8. isScrollGesturesEnabled(): Returns whether scroll gestures are enabled or not.

  9. setDoubleTapGesturesEnabled(boolean enabled): Enables or disables double-tap gestures on the map.

  10. isDoubleTapGesturesEnabled(): Returns whether double tap gestures are enabled or not.

  11. setQuickZoomGesturesEnabled(boolean enabled): Enables or disables quick zoom gestures on the map.

  12. isQuickZoomGesturesEnabled(): Returns whether quick zoom gestures are enabled or not.

  13. setHorizontalScrollGesturesEnabled(boolean enabled): Enables or disables horizontal scroll gestures on the map.

  14. isHorizontalScrollGesturesEnabled(): Returns whether horizontal scroll gestures are enabled or not.

  15. setFlingVelocityAnimationEnabled(boolean enabled): Enables or disables fling velocity animation.

  16. isFlingVelocityAnimationEnabled(): Returns whether fling velocity animation is enabled or not.

  17. setScaleVelocityAnimationEnabled(boolean enabled): Enables or disables scale velocity animation.

  18. isScaleVelocityAnimationEnabled(): Returns whether scale velocity animation is enabled or not.

  19. setRotateVelocityAnimationEnabled(boolean enabled): Enables or disables rotate velocity animation.

  20. isRotateVelocityAnimationEnabled(): Returns, whether rotate velocity animation, is enabled or not.

  21. setFocalPoint(PointF point): Sets the focal point on the map for gestures.

  22. 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.

DIDN'T FIND WHAT YOU LOOKING FOR?