mapbox / mapbox/mapbox-maps-android
[gestures] Native fling ignores moderate pans and has no API to tune OverScroller friction
Nobody has claimed this yet.
- Dominant language
- Kotlin
- Stars
- 578
- Forks
- 161
- PR merge metrics
- No merged PRs in 30d
Description
Not sure if this is intended from the library or a bug.
## Environment
- Android OS version: All versions tested (SDK-level; not device-specific)
- Devices affected: All Android devices. Compared against iOS Maps SDK, where pan lift-off glides with `UIScrollView.DecelerationRate.normal`.
- Maps SDK Version: 11.27.0 (also present on 11.25+ where `useNativeFlingDeceleration` shipped; still no public knobs in 11.28)
## Observed behavior and steps to reproduce
1. Show a `MapView` with globe or mercator projection.
2. Enable native fling:
```kotlin
mapView.gestures.updateSettings {
scrollDecelerationEnabled = true
useNativeFlingDeceleration = true
}
```
3. Pan the map, then lift the finger.
**Without** `useNativeFlingDeceleration`: the map stops immediately on lift (legacy `easeTo`).
**With** it enabled (default still `false`):
- Fast flicks get some momentum, but it dies faster than iOS / Google Maps. `OverScroller` uses list-scroll friction (`ViewConfiguration.getScrollFriction()` ≈ `0.015`).
- Moderate / follow-through pans (density-normalized velocity ~300–1000) get **no** fling. The map hard-stops. `VELOCITY_THRESHOLD_IGNORE_FLING` is still inlined at `1000` ([PR #599](https://github.com/mapbox/mapbox-maps-android/pull/599)).
There is no public `GesturesSettings` field for friction or that threshold. Matching iOS today requires reflecting into `overScrollerFlingAnimator` / `overScroller` and wrapping `onFling`.
## Expected behavior
After a pan, lift-off should continue with physics-based deceleration, including moderate pans, similar to iOS `panDecelerationFactor` (~`0.998` / `UIScrollView.DecelerationRate.normal`).
Please either:
1. Expose `OverScroller` friction (and/or a map-appropriate default lower than `0.015`).
2. Lower or make configurable `VELOCITY_THRESHOLD_IGNORE_FLING` (Mapbox 9.5 / MapLibre used ~300).
3. Document that `useNativeFlingDeceleration` is required for any iOS-like glide, and that it is still `false` by default.
## Notes / preliminary analysis
`useNativeFlingDeceleration` in 11.25 replaced `easeTo` with `OverScroller`, which is the right direction, but two internals still make lift-off feel sticky:
1. **Friction.** Default `OverScroller` friction is for `ListView`, not a map. Lowering it to ~`0.006` via reflection gets much closer to iOS.
2. **Velocity gate.** `onFling` returns false below 1000, so `OverScroller` never starts for typical “drag then lift” gestures. Forcing `fling()` in the 300–1000 band restores those.
11.27’s “fling animation moving the camera in the opposite direction at low zoom” fix is separate and works. This is leftover physics, not that globe-direction bug.
Workaround we are using (fragile; depends on private fields):
- `useNativeFlingDeceleration = true`
- `overScroller.setFriction(0.006f)` via reflection
- If stock `onFling` returns false and `hypot(vx/density, vy/density)` is in `[300, 1000)`, call the internal animator `fling()` ourselves
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the native fling path through GesturesSettings, onFling, overScrollerFlingAnimator, and OverScroller, focusing on the 1000 velocity gate and default friction. Compare behavior for fast and moderate pans, then determine which requested resolution is appropriate: configurable friction or threshold, a changed default, or documentation. Done means the selected behavior is covered and the public API or documentation accurately describes it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, kotlin
- Domain
- mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100