ScrollView breaks RefreshControl on Android
See original GitHub issueWhen using ScrollView from rngh RefreshControl no longer works. This happens because on Android RefreshControl works by wrapping the ScrollView with a SwipeRefreshLayout component. This component should interact with the rngh gesture system since it needs to be able to interrupt and recognize simultaneously the ScrollView it wraps.
This can be kind of accomplished by wrapping RefreshControl with createNativeWrapper
on Android. This is the first part of the hack patch I have to fix this:
GestureComponents.js
});
},
+ get RefreshControl() {
+ if (Platform.OS === 'android') {
+ return memoizeWrap(ReactNative.RefreshControl, {
+ disallowInterruption: true,
+ shouldCancelWhenOutside: false,
+ });
+ } else {
+ return ReactNative.RefreshControl;
+ }
+ },
get Switch() {
return memoizeWrap(ReactNative.Switch, {
The problem then is that ScrollView sets disallowInterruption
to true, which means it cannot get interrupted or recognize with another gesture handler. Setting disallowInterruption
to false makes RefreshControl work but causes other issues like nested ScrollView will both scroll at the same time.
I tried playing with adding simultaneousHandlers
or waitFor
to the RefreshControl associated with the ScrollView but wasn’t able to get it working. Seems like disallowInterruption
takes priority over that.
So at this point I was mostly looking for a hack to get it working so I came up with:
NativeViewGestureHandler.java
import android.view.ViewGroup;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
public class NativeViewGestureHandler extends GestureHandler<NativeViewGestureHandler> {
private boolean mShouldActivateOnStart;
@@ -48,7 +50,7 @@ public class NativeViewGestureHandler extends GestureHandler<NativeViewGestureHa
}
}
- boolean canBeInterrupted = !mDisallowInterruption;
+ boolean canBeInterrupted = !shouldDisallowInterruptionBy(handler);
int state = getState();
int otherState = handler.getState();
@@ -62,9 +64,19 @@ public class NativeViewGestureHandler extends GestureHandler<NativeViewGestureHa
return state == STATE_ACTIVE && canBeInterrupted;
}
+ private boolean shouldDisallowInterruptionBy(GestureHandler handler) {
+ if (handler.getView() instanceof SwipeRefreshLayout) {
+ return false;
+ }
+ return mDisallowInterruption;
+ }
+
@Override
public boolean shouldBeCancelledBy(GestureHandler handler) {
- return !mDisallowInterruption;
+ if (handler.getView() instanceof SwipeRefreshLayout) {
+ return true;
+ }
+ return !shouldDisallowInterruptionBy(handler);
}
@Override
Basically it just special cases when the other handler is SwipeRefreshLayout so that even if mDisallowInterruption is true it will treat it like it was false.
Not sure how this could be properly fix, will keep using this hack for now.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:9
- Comments:28 (7 by maintainers)
Top GitHub Comments
I had the same issue with the scrollview from react-native-gesture-handler tried so many methods finally i just switched to scrollview from react-native to solve this issue
I debugged this for a bit and looks like refresh control is getting interrupted by scroll view (that has
disallowInterruption: true
). I’m considering some solutions to this problem but I want to avoid adding a special case for ScrollView.As a workaround for now, you can wrap Refresh Control in NativeView Gesture handler and use
waitFor
with it:Full example