Skip to content

[gestures] Native fling ignores moderate pans and has no API to tune OverScroller friction #2729

Description

@ferasahmad

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:
mapView.gestures.updateSettings {
    scrollDecelerationEnabled = true
    useNativeFlingDeceleration = true
}
  1. 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).

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🪲Something isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions