callstack / react-native-pager-view

React Native wrapper for the Android ViewPager and iOS UIPageViewController.
MIT License
2.65k stars 410 forks source link

Content touches on scroll end #164

Closed Bardiamist closed 3 years ago

Bardiamist commented 4 years ago

Bug

Content touches on scroll end on Android (tested on Android 5.0)

Similar issue was on on iOS when ViewPager was introduced on iOS. And you fixed that.

I have 3 pages. Every page has FlatList. Every Flat list row is TouchableOpacity. On scroll end fires onPress of TouchableOpacity.

Library version: 4.0.0 breaks it. It works on < 4.0.0

troZee commented 4 years ago

@Bardiamist Could you provide more description ?

Bardiamist commented 4 years ago

Simular issue was on on iOS when ViewPager was introdused on iOS. And you fixed that.

I have 3 pages. Every page has FlatList. Every Flat list row is TouchableOpacity. On scroll end fires onPress of TouchableOpacity.

troZee commented 4 years ago

This snippet https://github.com/react-native-community/react-native-viewpager/issues/73#issuecomment-562950417 should be also applied for android ?

Could you provide code example, that I can check for android ?

troZee commented 4 years ago

Do you know, if issue exist for < 4.0.0 ?

Bardiamist commented 4 years ago

Only on 4.0.0. I updated, tested, saw it bug, and rolled back to 3.3.0.

elkinjosetm commented 4 years ago

Same here, but not only on scroll end, I'm also having the same issue when swiping left or right on Android. I moved back to 3.3.0, and it's working fine.

Bardiamist commented 4 years ago

When I say "scoll end", I mean swiping left or right.

lukebars commented 4 years ago

Same issue, if a page has TouchableOpacity inside, it fires onPress of that TouchableOpacity every swipe that is touching that touchable. v3 works good.

troZee commented 4 years ago

@lukebars @Bardiamist is it somehow related to https://github.com/react-native-community/react-native-viewpager/issues/11 ?

troZee commented 4 years ago

I started investigation here: https://github.com/react-native-community/react-native-viewpager/tree/164

Probably I found a solution.

taduyde commented 4 years ago

the same issue, is there any update for this? I moved back to 3.3.0, and it's working fine

kabus202 commented 4 years ago

same here downgrade to 3.3.0 and everything is working fine

oliverdolgener commented 4 years ago

yep same here: ViewPager v4 with touchable items will trigger onPress() when scrolling. Worked fine on v3.3 only tested on Android

Fortidude commented 4 years ago

I had this issue on android, while iOS was perfectly fine.

The solution is to use TouchableOpacity component from react-native-gesture-handler.

import { TouchableOpacity } from 'react-native-gesture-handler'

It handles touches / gestures in a bit different way:

These touchables and their feedback behavior are deeply integrated with native gesture ecosystem and could be connected with other native components (e.g. ScrollView) and Gesture Handlers easily and in a more predictable way, which follows native apps' behavior.

henr commented 4 years ago

same

kabus202 commented 4 years ago

guys any solution for this problem?

srfaytkn commented 4 years ago

+1

lukebars commented 4 years ago

@troZee did you manage to solve this?

wong2 commented 4 years ago
import { TouchableOpacity } from 'react-native-gesture-handler'

doesn't work for me, it makes the view pager un-swipeable

wong2 commented 4 years ago

Downgraded to 3.3.0, it works fine

Relax594 commented 4 years ago

Using a FlatList with Viewpager 4.0+ on Android makes scrolling unusable. Trying to scroll down and it constantly "steals away" the scroll event and scrolls left / right. Going back to 3.3.0 is working fine. However the 3.x has this issue on iOS which wants me to upgrade..

For example using react-native-pdf to show a PDF (which uses a FlatList). Its nearly unscrollable on Android in 4.0+, however totally fine on 3.3.0.

ChenhuaWANG22 commented 4 years ago

I had this issue on android, while iOS was perfectly fine.

The solution is to use TouchableOpacity component from react-native-gesture-handler.

import { TouchableOpacity } from 'react-native-gesture-handler'

It handles touches / gestures in a bit different way:

These touchables and their feedback behavior are deeply integrated with native gesture ecosystem and could be connected with other native components (e.g. ScrollView) and Gesture Handlers easily and in a more predictable way, which follows native apps' behavior.

I have this scrolling and touching conflict issue on iOS. I use above method to fix it on iOS and later discover these touchables from react-native-gesture-handler don't working on Android currently. see https://github.com/software-mansion/react-native-gesture-handler/issues/578

at the end, I have to do below : (

import { Platform } from 'react-native';

let TouchableHighlight,TouchableOpacity;
if (Platform.OS === 'ios') {
    ({ TouchableHighlight,TouchableOpacity } = require('react-native-gesture-handler'));
} else {
    ({ TouchableHighlight,TouchableOpacity } = require('react-native'));
}
felipeViana commented 4 years ago

I'm having this problem too, only in android. Looks like @troZee commit worked for me, i'll test it more thoroughly to make sure it solved the problem and reply here later.

update: After more testing i've encountered many other problems after testing with @troZee fix. I'm using FlatList from RN now and it's working fine.

ease-space commented 4 years ago

Same issue good work in v 3.3.1 on android , v4 if use TouchableHighlight from gesture handler hot work swipe scroll tabs, if from react-native swipe scroll handled onPress on flatlist item on android and ios (ios have this bug too on real device emulator not reproduce, but small count incident rundom triger onPress in v3.3.1 and latest v4) my solution not use this library until this problem is resolved

troZee commented 4 years ago

Can someone test above PR ?

After more testing i've encountered many other problems after testing with @troZee fix.

What problems @felipeViana ?

lukebars commented 4 years ago

@troZee github:react-native-community/react-native-viewpager#@troZee/fix-on-scroll doesn't seem to fix the problem.

troZee commented 4 years ago

Could you provide a code snippet, which I can test ?

lukebars commented 4 years ago

@troZee Just a FlatList full of big TouchableOpacity's navigating to other screen. I can't provide a working code snippet right now, but if you won't be able to reproduce it, I could take some time to prepare one.

troZee commented 4 years ago

@lukebars can you also check the v. 3.3.0 ?

lukebars commented 4 years ago

@troZee We're using 3.3.0 in prod right now and that works like a charm.

Relax594 commented 4 years ago

I can confirm the PR does not work. v3.3.1 is fine.

Relax594 commented 4 years ago

@troZee you can test it using this snippet. Try to scroll the pdf on the second page on Android. Best of luck :+1: This works fine in v3.3.1, however using v4.x makes it unscrollable. iOS is totally fine on both versions. react-native-pdf uses a FlatList in the background.

App.js

import React from "react";
import { StyleSheet, Dimensions, View, Text } from "react-native";

import ViewPager from "@react-native-community/viewpager";
import Pdf from "react-native-pdf";

const App = () => {
  const source = {
    uri: "http://samples.leanpub.com/thereactnativebook-sample.pdf",
    cache: true,
  };
  //const source = require('./test.pdf');  // ios only
  //const source = {uri:'bundle-assets://test.pdf'};

  //const source = {uri:'file:///sdcard/test.pdf'};
  //const source = {uri:"data:application/pdf;base64,JVBERi0xLjcKJc..."};

  return (
    <ViewPager style={styles.viewPager} initialPage={0}>
      <View key="1">
        <Text>First page</Text>
      </View>
      <View key="2" style={styles.container}>
        <Pdf source={source} style={styles.pdf} />
      </View>
    </ViewPager>
  );
};

const styles = StyleSheet.create({
  viewPager: {
    flex: 1,
  },
  container: {
    flex: 1,
    justifyContent: "flex-start",
    alignItems: "center",
    marginTop: 25,
  },
  pdf: {
    flex: 1,
    width: Dimensions.get("window").width,
    height: Dimensions.get("window").height,
  },
});

export default App;

Package.json

"dependencies": {
    "react": "16.13.1",
    "react-native": "0.63.2",
    "react-native-pdf": "6.2.0",
    "@react-native-community/progress-bar-android": "1.0.3",
    "@react-native-community/progress-view": "1.1.1",
    "@react-native-community/viewpager": "4.1.6",
    "rn-fetch-blob": "0.12.0"
  },
felipeViana commented 4 years ago

After more testing i've encountered many other problems after testing with @troZee fix.

What problems @felipeViana ?

I'm using react-native-tab-view to render tabs at home screen and some internal screens as well. The tab content at home would be blank after going in the internal tab screens then going back to home.

Other people tested and related different problems but i ended up going for another solution and didn't try to find the root cause for these problems.

viniaxt commented 4 years ago

Bug

Content touches on scroll end on Android (tested on Android 5.0)

Similar issue was on on iOS when ViewPager was introduced on iOS. And you fixed that.

I have 3 pages. Every page has FlatList. Every Flat list row is TouchableOpacity. On scroll end fires onPress of TouchableOpacity.

Library version: 4.0.0 breaks it. It works on < 4.0.0

I do found a workaround to fix it

I changed my initial structure - ViewPager as father with FlatList - as as so as my father component is a flatlist. I did that because i can use flatlist onScroll props to disable touch from rendered items. You can use this idea to do what you want, since FlatList has really powerfull with props

  const [disabledTouch, setDisabledTouch] = useState(false)

  return (
    <FlatList
      data={data}
      // fires when user begin/end drag
      // you can use other props, such as onMomentumScroll etc
      onScrollBeginDrag={() => setDisabledTouch(true)}
      onScrollEndDrag={() => setDisabledTouch(false)}
      renderItem={({ item }) => (
        <>
          <ViewPager> {item.map() ....}</ViewPager>
          <TouchableWithoutFeedback disabled={disabledTouch} />
        </>
      )}
    />
  )
Sofianio commented 3 years ago

I hope this gets fixed soon, I am uncomfortable using 2 different TouchableOpacity for iOS and Android

ease-space commented 3 years ago

when fixed this issue?

luogao commented 3 years ago

I found a solution ref this comment https://github.com/react-native-community/react-native-viewpager/issues/73#issuecomment-562950417


                 <ViewPager
                    onMoveShouldSetResponderCapture={() => true}
                >
                    // ... your content
                </ViewPager>

and if you are using FlatList or ScrollView with ViewPager change import { FlatList } from 'react-native' to import { FlatList } from 'react-native-gesture-handler'

Thank you @ljsalm089 @Fortidude

tankers746 commented 3 years ago

I was able to solve this by wrapping ViewPager in <NativeViewGestureHandler disallowInterruption={true}> from react-native-gesture-handler

E.g.

import { NativeViewGestureHandler } from 'react-native-gesture-handler';

<NativeViewGestureHandler
    disallowInterruption={true}>
    <ViewPager>
        {children}
    </ViewPager>
</NativeViewGestureHandler>

This is how FlatList exported from react-native-gesture-handler works as well.

elkinjosetm commented 3 years ago

Updating to v4.2.x seems to fix this issue for me.

Based on the release notes, the Android version was reverted to the latest stable codebase (v3.3.0).

troZee commented 3 years ago

@elkinjosetm Yep, but a problem still remain on next version

troZee commented 3 years ago

This issue has been fixed in 5.0.5

LevanKvirkvelia commented 3 years ago

"react-native-pager-view": "^5.1.7"

Have this problem on iOS and android

tothvoj-gl commented 2 years ago

Hi! 👋

Firstly, thanks for your work on this project! 🙂

Today I used patch-package to patch react-native-pager-view@5.4.0 for the project I'm working on.

Here is the diff that is a workaround for the problem with too sensitive swiping on android (source: https://medium.com/@al.e.shevelev/how-to-reduce-scroll-sensitivity-of-viewpager2-widget-87797ad02414)

The proper fix I guess would be https://bladecoder.medium.com/fixing-recyclerview-nested-scrolling-in-opposite-direction-f587be5c1a04. But it hasn't made to android repo yet.

diff --git a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt
index 6e2a436..7c757be 100644
--- a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt
+++ b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt
@@ -1,6 +1,7 @@
 package com.reactnativepagerview

 import android.view.View
+import androidx.recyclerview.widget.RecyclerView
 import androidx.viewpager2.widget.ViewPager2
 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 import com.facebook.infer.annotation.Assertions
@@ -24,6 +25,17 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
     return REACT_CLASS
   }

+  fun ViewPager2.reduceDragSensitivity(value: Int) {
+    val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
+    recyclerViewField.isAccessible = true
+    val recyclerView = recyclerViewField.get(this) as RecyclerView
+
+    val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
+    touchSlopField.isAccessible = true
+    val touchSlop = touchSlopField.get(recyclerView) as Int
+    touchSlopField.set(recyclerView, touchSlop*value)
+  }
+
   override fun createViewInstance(reactContext: ThemedReactContext): ViewPager2 {
     val vp = ViewPager2(reactContext)
     vp.adapter = ViewPagerAdapter()
@@ -134,6 +146,11 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
     }
   }

+  @ReactProp(name = "scrollSensitivity", defaultInt = 2)
+  fun setScrollSensitivity(viewPager: ViewPager2, value: Int) {
+    viewPager.reduceDragSensitivity(value)
+  }
+
   @ReactProp(name = "orientation")
   fun setOrientation(viewPager: ViewPager2, value: String) {
     viewPager.orientation = if (value == "vertical") ViewPager2.ORIENTATION_VERTICAL else ViewPager2.ORIENTATION_HORIZONTAL
diff --git a/node_modules/react-native-pager-view/lib/typescript/types.d.ts b/node_modules/react-native-pager-view/lib/typescript/types.d.ts
index e14b01b..a67a19b 100644
--- a/node_modules/react-native-pager-view/lib/typescript/types.d.ts
+++ b/node_modules/react-native-pager-view/lib/typescript/types.d.ts
@@ -4,6 +4,7 @@ export declare type TransitionStyle = 'scroll' | 'curl';
 export declare type Orientation = 'horizontal' | 'vertical';
 export declare type OverScrollMode = 'auto' | 'always' | 'never';
 export declare type PageScrollState = 'idle' | 'dragging' | 'settling';
+export type ScrollSensitivity = 2|3|4|5|6|7|8;
 export declare type PagerViewOnPageScrollEvent = ReactNative.NativeSyntheticEvent<PagerViewOnPageScrollEventData>;
 export interface PagerViewOnPageScrollEventData {
     position: number;
@@ -28,6 +29,13 @@ export interface PagerViewProps {
      * The default value is true.
      */
     scrollEnabled?: boolean;
+    /**
+     * Higher the `scrollSensitivity` value, more the effort to swipe. 
+     * Adjust multiplier from 2-8 until you find the sweet spot.
+     *  
+     * Only supported on Android.
+     */
+    scrollSensitivity?: ScrollSensitivity;
     /**
      * Executed when transitioning between pages (ether because of animation for
      * the requested page change or when user is swiping/dragging between pages)
diff --git a/node_modules/react-native-pager-view/src/types.ts b/node_modules/react-native-pager-view/src/types.ts
index 72cf02c..13e6be1 100644
--- a/node_modules/react-native-pager-view/src/types.ts
+++ b/node_modules/react-native-pager-view/src/types.ts
@@ -5,6 +5,7 @@ export type TransitionStyle = 'scroll' | 'curl';
 export type Orientation = 'horizontal' | 'vertical';
 export type OverScrollMode = 'auto' | 'always' | 'never';
 export type PageScrollState = 'idle' | 'dragging' | 'settling';
+export type ScrollSensitivity = 2|3|4|5|6|7|8;

 export type PagerViewOnPageScrollEvent = ReactNative.NativeSyntheticEvent<PagerViewOnPageScrollEventData>;
 export interface PagerViewOnPageScrollEventData {
@@ -35,6 +36,14 @@ export interface PagerViewProps {
    */
   scrollEnabled?: boolean;

+  /**
+   * Higher the `scrollSensitivity` value, more the effort to swipe. 
+   * Adjust multiplier from 2-8 until you find the sweet spot.
+   *  
+   * Only supported on Android.
+   */
+  scrollSensitivity?: ScrollSensitivity;
+
   /**
    * Executed when transitioning between pages (ether because of animation for
    * the requested page change or when user is swiping/dragging between pages)

This issue body was partially generated by patch-package.