fix(android): MarkerView touch/press on new architecture (Fabric)#4176
Open
fix(android): MarkerView touch/press on new architecture (Fabric)#4176
Conversation
…ure (Fabric) On Android with the new React Native architecture, Mapbox positions view annotations via setTranslationX/Y — a render-layer transform that is invisible to Fabric's shadow tree. This caused UIManager.measure to return (0,0) for every MarkerView, so Pressable/TouchableOpacity hit-testing failed, onPress was never fired, and interactive children (sliders, switches, buttons) didn't respond to touches. Fix: - Override setTranslationX/Y in RNMBXMarkerViewContent to intercept Mapbox's position updates and fire a topAnnotationPosition event to JS. - Deduplicate events to prevent a feedback loop when Fabric re-applies the same transform prop back to setTranslationX/Y. - Override dispatchTouchEvent to call requestDisallowInterceptTouchEvent(true), preventing the parent MapView's pan/zoom gesture from stealing MOVE events and sending CANCEL to child Pressables. - In JS (MarkerView.tsx), receive the position event and apply the translation as a React transform on RNMBXMarkerView, keeping the Fabric shadow tree in sync with Mapbox's native positioning so hit regions are correct. - Register the event via AbstractEventEmitter in RNMBXMarkerViewContentManager. - Add an interactive example (sliders, switch, counter, TextInput, Pressable) to the MarkerView example to exercise complex interactivity. iOS is unaffected: Mapbox iOS positions annotations via frame assignment, which Fabric tracks correctly without any workaround.
3c4d8d9 to
e63f30c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3832
Fixes #3934
Fixes #3440
Fixes #3981
Problem
On Android with the new React Native architecture (Fabric/TurboModules), any interactive content inside a
MarkerView— buttons, sliders, switches,TextInput— silently failed. Touches appeared to land nowhere,onPresswas never fired, and press feedback (opacity, scale) didn't show.Root cause: Mapbox positions view annotations by calling
setTranslationX/Ydirectly on the native view. This is a render-layer transform — it moves the view visually but does not update the view's Yoga layout position. Fabric's shadow tree only tracks layout position, soUIManager.measure()always returned(0, 0), making every hit-region wrong.A second issue: the
MapView's own gesture recogniser was interceptingMOVEevents mid-sequence (afterDOWN), sendingCANCELto childPressables and suppressingonPresseven in cases where the hit-test was correct.iOS is unaffected — Mapbox iOS positions annotations via
frameassignment, which Fabric reads correctly.Fix
Android —
RNMBXMarkerViewContent.ktsetTranslationX/Yto intercept Mapbox's position updates and fire atopAnnotationPositionevent to JS.lastReportedTx/Tyto prevent a feedback loop when Fabric re-applies the same transform prop back tosetTranslationX/Y.dispatchTouchEventto callrequestDisallowInterceptTouchEvent(true), preventing the parentMapViewpan/zoom gesture from stealingMOVEevents and cancelling child touches.Android —
RNMBXMarkerViewContentManager.ktAbstractEventEmitterand registertopAnnotationPosition → onAnnotationPosition.Codegen spec —
RNMBXMarkerViewContentNativeComponent.tsonAnnotationPositionas aDirectEventHandlerso Fabric's bridge knows about it.JS —
MarkerView.tsxonAnnotationPositionevents and apply the translation as a ReacttransformonRNMBXMarkerView, keeping the Fabric shadow tree in sync with Mapbox's actual on-screen position.PixelRatio.get()divide: AndroidtranslationX/Yis in device pixels; React transforms expect logical pixels.Example —
MarkerView.tsxInteractiveMarkerContentmarker with two sliders, a switch, a counter, aTextInput, and aPressablebutton to exercise the full range of complex interactivity inside aMarkerView.Test plan
onPressfires, blue flash visible