Skip to content

Commit da2c028

Browse files
authored
fix(Android): implement ReactPointerEventsView to prevent header config from intercepting touches (#2796)
## Description Closes #2794 We set `visibility = GONE` for the header config in HostTree (HT), however this only prevents the Android framework from setting header config frame, and does not prevent `ReactNative` from setting non-zero frame. When it has non-zero frame, react recognizes it as a touch target. ## Changes I've implemented `ReactPointerEventsView` (returning `box-none`) making sure that RN won't treat it as touch target. I considered another approach - setting the frame to zero or not letting react native to set the frame on the view - it also works, however requires cumbersome implementation on Android API level < 29, since `suppressLayout` method is available since 29. On lower API levels you can not really ignore the frame - you can call `layout` with 0 width in `onLayout` of the header config... ## Test code and steps to reproduce `HeaderOptions` screen in `Example` app. Tested it on both architectures & with `headerShown: false/true`. ## Checklist - [ ] Ensured that CI passes
1 parent 91400bc commit da2c028

4 files changed

Lines changed: 16 additions & 5 deletions

File tree

android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ class ScreenStackFragment :
480480
constructor(context: Context, fragment: ScreenStackFragment) : this(
481481
context,
482482
fragment,
483-
ScreensCoordinatorLayoutPointerEventsImpl(),
483+
PointerEventsBoxNoneImpl(),
484484
)
485485

486486
override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets = super.onApplyWindowInsets(insets)
@@ -543,7 +543,13 @@ class ScreenStackFragment :
543543
}
544544
}
545545

546-
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
546+
override fun onLayout(
547+
changed: Boolean,
548+
l: Int,
549+
t: Int,
550+
r: Int,
551+
b: Int,
552+
) {
547553
super.onLayout(changed, l, t, r, b)
548554

549555
if (fragment.screen.usesFormSheetPresentation()) {

android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment
1515
import com.facebook.react.ReactApplication
1616
import com.facebook.react.bridge.JSApplicationIllegalArgumentException
1717
import com.facebook.react.bridge.ReactContext
18+
import com.facebook.react.uimanager.ReactPointerEventsView
1819
import com.facebook.react.uimanager.UIManagerHelper
1920
import com.facebook.react.views.text.ReactTypefaceUtils
2021
import com.swmansion.rnscreens.events.HeaderAttachedEvent
@@ -23,7 +24,11 @@ import kotlin.math.max
2324

2425
class ScreenStackHeaderConfig(
2526
context: Context,
26-
) : FabricEnabledHeaderConfigViewGroup(context) {
27+
private val pointerEventsImpl: ReactPointerEventsView
28+
) : FabricEnabledHeaderConfigViewGroup(context), ReactPointerEventsView by pointerEventsImpl {
29+
30+
constructor(context: Context): this(context, pointerEventsImpl = PointerEventsBoxNoneImpl())
31+
2732
private val configSubviews = ArrayList<ScreenStackHeaderSubview>(3)
2833
val toolbar: CustomToolbar
2934
var isHeaderHidden = false // named this way to avoid conflict with platform's isHidden

android/src/versioned/pointerevents/77/com/swmansion/rnscreens/ScreensCoordinatorLayoutPointerEventsImpl.kt renamed to android/src/versioned/pointerevents/77/com/swmansion/rnscreens/PointerEventsBoxNoneImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.swmansion.rnscreens
33
import com.facebook.react.uimanager.PointerEvents
44
import com.facebook.react.uimanager.ReactPointerEventsView
55

6-
internal class ScreensCoordinatorLayoutPointerEventsImpl : ReactPointerEventsView {
6+
internal class PointerEventsBoxNoneImpl() : ReactPointerEventsView {
77
// We set pointer events to BOX_NONE, because we don't want the ScreensCoordinatorLayout
88
// to be target of react gestures and effectively prevent interaction with screens
99
// underneath the current screen (useful in `modal` & `formSheet` presentation).

android/src/versioned/pointerevents/latest/com/swmansion/rnscreens/ScreensCoordinatorLayoutPointerEventsImpl.kt renamed to android/src/versioned/pointerevents/latest/com/swmansion/rnscreens/PointerEventsBoxNoneImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.swmansion.rnscreens
33
import com.facebook.react.uimanager.PointerEvents
44
import com.facebook.react.uimanager.ReactPointerEventsView
55

6-
internal class ScreensCoordinatorLayoutPointerEventsImpl() : ReactPointerEventsView {
6+
internal class PointerEventsBoxNoneImpl() : ReactPointerEventsView {
77
// We set pointer events to BOX_NONE, because we don't want the ScreensCoordinatorLayout
88
// to be target of react gestures and effectively prevent interaction with screens
99
// underneath the current screen (useful in `modal` & `formSheet` presentation).

0 commit comments

Comments
 (0)