Skip to main content

Gestures

A newer version of the Maps SDK is available
This page uses v9.7.1 of the Mapbox Maps SDK. A newer version of the SDK is available. Learn about the latest version, v11.3.0, in the Maps SDK documentation.

The Mapbox Gestures for Android library is used inside of the Mapbox Maps SDK for Android for gesture detection based on user's input. This library wraps GestureDetectorCompat and ScaleGestureDetector as well as introduces implementation of rotate, move, shove, and tap gesture detectors.

The library is implemented in both the Mapbox Maps SDK for Android and the library's test application.

See the API documentation for more details on using the Gestures library.

Installation

Gradle

  1. Open Android Studio.
  2. Open up your application's build.gradle file.
  3. Make sure that your project's minSdkVersion is at API 14 or higher.
  4. Under dependencies, add a new build rule for the latest mapbox-gestures-android version (see below).
  5. Click on Sync Project with Gradle Files near the toolbar in Android Studio.
implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.5.1'

Note: ProGuard directives are included in the Android dependencies to preserve the required classes.

The library's releases are published to Maven Central. If you'd like to test out the master branch build, use SNAPSHOTs which are available on Sonatype.

// In the root build.gradle file
repositories {
mavenCentral()
maven { url "http://oss.sonatype.org/content/repositories/snapshots/" }
}
// In the app build.gradle file
dependencies {
implementation 'com.mapbox.mapboxsdk:mapbox-android-gestures:0.6.0-SNAPSHOT'
}

Standalone usage

You have to instantiate AndroidGestureManager to start any gestures processing.

Set any gesture listeners that you are interested in and pass all MotionEvent objects from your Activity/Fragment to AndroidGestureManager#onTouchEvent().

Mutually-exclusive gestures

Mutually-exclusive gestures are possible thanks to the AndroidGestureManager class, which serves as a single entry point to all gesture detectors.

This means that you can pass a list of GestureType sets. Whenever a gesture is detected, it will check whether there are any ProgressiveGestures started that are contained within the same set. If there aren't any, the listener for our detected gesture will not be notified.

You can pass mutually exclusive gesture sets in a constructor of AndroidGestureManager or with AndroidGestureManager#setMutuallyExclusiveGestures().

For example:

val mutuallyExclusive1 = HashSet<Int>()
mutuallyExclusive1.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE)
mutuallyExclusive1.add(AndroidGesturesManager.GESTURE_TYPE_SCROLL)

val mutuallyExclusive2 = HashSet<Int>()
mutuallyExclusive2.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE)
mutuallyExclusive2.add(AndroidGesturesManager.GESTURE_TYPE_SCALE)

val androidGesturesManager = AndroidGesturesManager(this@MatrixApiActivity, mutuallyExclusive1, mutuallyExclusive2)

The first set makes it certain that when a shove gesture is detected, you will no longer be notified about the scroll gestures. The shove gesture will be able to execute because scroll is not a ProgressiveGesture. In the second set, when either a shove or scale is detected, you won't be notified about the other one until the first gesture finishes.

Thresholds

You can set thresholds for supported gestures, which means that a gesture detector won't fire until it meets the threshold (like minimum rotation angle). This allows you to personalize the gestures experience how you would like.

Set thresholds using dimen values, rather than raw pixels, to accommodate for various screen sizes and pixel densities across Android devices. For example:

androidGesturesManager?.standardScaleGestureDetector?.setSpanSinceStartThresholdResource(R.dimen.scaleSpanSinceStartThreshold)

For thresholds that are not expressed in pixels:

androidGesturesManager?.rotateGestureDetector?.angleThreshold = ROTATE_ANGLE_THRESHOLD

Velocity

Each progressive gesture with its respective #onEnd() callback will provide X velocity and Y velocity of the gesture at the moment when the pointer leaves the screen.

Enable/disable and interrupt

Every gesture detector can be enabled/disabled at any time using the #setEnabled() method.

Additionally, every progressive gesture can be interrupted, which will force it to meet start conditions again to resume. A popular use case would be to increase the gesture's threshold when another gesture is detected:

override fun onScaleBegin(detector: StandardScaleGestureDetector): Boolean {
// forbid movement when scaling
androidGesturesManager?.moveGestureDetector.isEnabled = false // this interrupts a gesture as well

// increase rotate angle threshold when scale is detected, then interrupt to force re-check
val rotateGestureDetector = androidGesturesManager.rotateGestureDetector
rotateGestureDetector.angleThreshold = ROTATION_THRESHOLD_WHEN_SCALING
rotateGestureDetector.interrupt()

return true
}

override fun onScale(detector: StandardScaleGestureDetector): Boolean {
val scaleFactor = detector.scaleFactor

return true
}

override fun onScaleEnd(detector: StandardScaleGestureDetector) {
// revert thresholds values
val rotateGestureDetector = androidGesturesManager?.rotateGestureDetector
rotateGestureDetector.angleThreshold = Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
}

Detectors

With this library you will be able to recognize gestures using detectors provided by the AndroidX library and more.

StandardGestureDetector

Wraps GestureDetectorCompat exposed via the AndroidX library that recognizes gestures like tap, double tap or scroll.

StandardScaleGestureDetector

Wraps ScaleGestureDetector exposed via the MotionEvent class that recognizes scale/pinch gesture.

MultiFingerTapGestureDetector

Gesture detector that tell listeners whenever a multi finger tap occurred and how many fingers where involved.

RotateGestureDetector

A detector that finds the angle difference between previous and current line made with two pointers (fingers).

ShoveGestureDetector

Detects a vertical movement of two pointers if they are placed within a certain horizontal angle.

MoveGestureDetector

Behaves similarly to #onScroll() contained in the StandardGestureDetector, but, it's a ProgressiveGesture that enables better filtering options, as well as thresholds.

SidewaysShoveGesturesDetector

A sibling of the ShoveGestureDetector, but, it recognizes the two-finger shove gesture executed in a horizontal, rather than vertical, line.

Usage with the Maps SDK

This library is shipped as a part of the Mapbox Maps SDK for Android and you don't have to declare any other dependencies.

To get the AndroidGesturesManager object which holds references to all the gesture detectors, use:

val gesturesManager = mapboxMap.gesturesManager

To react to the user's input, you can register several different listeners that will be notified whenever a user interacts with a Mapbox map.

OnMapClickListener:

mapboxMap.addOnMapClickListener { point ->

}

OnMoveListener:

mapboxMap.addOnMoveListener(object : MapboxMap.OnMoveListener {
override fun onMoveBegin(detector: MoveGestureDetector) {
// user started moving the map
}

override fun onMove(detector: MoveGestureDetector) {
// user is moving the map
}

override fun onMoveEnd(detector: MoveGestureDetector) {
// user stopped moving the map
}
})

You can see a MoveGestureDetector object being passed in the callbacks above. This class is the underlying detector from the Mapbox Gestures for Android library.

Other popular listener interfaces that you can implement are:

  • OnMapLongClickListener fires when the map is touched and a single finger is held down on the map.
  • OnRotateListener fires when the map is rotated.
  • OnScaleListener fires when the map is zoomed in or out.
  • OnShoveListener fires when map is tilted.

Custom AndroidGesturesManager

The Maps SDK offers a MapboxMap#setGesturesManager() method. If you create a custom AndroidGesturesManager object, you can use it as a parameter for that method and for the custom mapping experience that you want to create.