Open ferrannp opened 4 years ago
@ferrannp, What about this ? I haven't these devices, only tested on my 5 devices(4.4 - 9.0) :(
Thanks for the reference @devvit! I'll be trying it tomorrow.
Mmm for now still that did not really help @devvit. That just seems to play with the threshold the page needs to be moved to swipe... But I think the problem here is different. If you drag very small distance but fast (with power), it should swipe to the next page. Still investigating... By the way, I was able to reproduce it on an emulator (Note 9 Android 9) using https://developer.samsung.com/remotetestlab/. The only drawback is that every time you want to try something you need to build the production apk.
Ok, I can reproduce the issue with the Android emulator (good to find a solution) on API level 28 (Android P). It seems to work before and after but not on Android P (most popular nowadays).
@ferrannp, Make ViewPager snap with shorter drag
My reflection solution, you can change the value of mMinimumVelocity and mFlingDistance.
public void setOrientation(boolean vertical) {
mVertical = vertical;
if (!mVertical) return;
// Make page transit vertical
setPageTransformer(true, new VerticalPageTransformer());
// Nested scroll issue, follow the link
// https://stackoverflow.com/questions/46828920/vertical-viewpager-with-horizontalscrollview-inside-fragment
mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceY) > Math.abs(distanceX);
}
});
// reflection
try {
Field mFlingDistance = ViewPager.class.getDeclaredField("mFlingDistance");
mFlingDistance.setAccessible(true);
mFlingDistance.setInt(this, 10);
Field mMinimumVelocity = ViewPager.class.getDeclaredField("mMinimumVelocity");
mMinimumVelocity.setAccessible(true);
mMinimumVelocity.setInt(this, 5);
} catch (Exception e) {
Log.e("###", e.getMessage());
}
}
Could you try straya's answer? I'm not sure that.
@devvit I found a a perfect working solution modifying the source code. https://android.googlesource.com/platform/frameworks/support/+/5b614a46f6ffb3e9ca5ab6321c12412550a4e13a/viewpager/src/main/java/androidx/viewpager/widget/ViewPager.java#2251. Instead of getXVelocity
, if pager is vertical would be getYVelocity
.
Can we achieve this by reflection without copying the whole source code?
Looks impossible 😓, just change mMinimumVelocity
.
Or maybe we commit a PR to android master dev 😅
Or I'll fork the internal code here (it will not change anyway as they are with ViewPager2
now). I'll prepare a PR.
Yes, fork it, freeze it, until we migrate to VP2
we can close it, when https://github.com/react-native-community/react-native-viewpager/pull/139 will be merged.
@troZee @ferrannp Is there any workaround to use vertical ViewPager now? I was so excited about native feel viewpager on react native, but found out that it's broken on Android...😿
PS: Thanks for working on it!
@todorone you can wait for https://github.com/react-native-community/react-native-viewpager/pull/139 or you can use this branch for now https://github.com/react-native-community/react-native-viewpager/tree/fix/vertical-pager-android-9-bug.
@todorone or you can create patch-package of #139. I need to fix one thing. Ofc I am not guarantee, that it will work, but you can try.
PS: Thanks for working on it!
<3
@troZee @ferrannp Thanks for prompt feedback, you guys are awesome.
Just tried both fix/vertical-pager-android-9-bug
branch and also viewpager2
branch. Both of them didn't help, I still need to swipe with finger for a almost full height distance to make vertical transition happen. Checked on both horizontal and vertical device alignment, Android 9 Mi 5 Note device(Emulator with Android 10 works perfectly)...
@todorone you can use a custom hook to handle touch events by yourself like below:
// the hook
import React, {useState} from 'react';
function TouchGesture(ref) {
const [duration, setDuration] = useState(0);
const [points, setPoints] = useState([0, 0]);
function onTouchStart(e) {
const {pageX, pageY} = e.nativeEvent;
setDuration(Date.now());
setPoints([pageX, pageY]);
}
function onTouchEnd(e) {
const {pageX, pageY} = e.nativeEvent;
if (Date.now() - duration > 200 || Math.abs(points[0] - pageX) > 50) {
return;
}
// this just an example
// you can use viewPager's position (add a onPageSelected function there) to judge which page you'll set;
if (points[1] - pageY > 30) {
ref?.current.setPage(1);
} else if (pageY - points[1] > 30) {
ref?.current.setPage(0);
}
}
return {onTouchEnd, onTouchStart};
}
export default TouchGesture;
// usage
const viewPagerRef = useRef(null);
const {onTouchEnd, onTouchStart} = useTouchGesture(viewPagerRef);
const handleTouchEnd = useCallback(e => onTouchEnd(e), [onTouchEnd]);
const handleTouchStart = useCallback(e => onTouchStart(e), [onTouchStart]);
......
<ViewPager
ref={viewPagerRef}
scrollEnabled={false}
orientation="vertical"
pageMargin={0}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
style={styles.viewPager}
initialPage={0}>
......
</ViewPager>
Swiping for a short distance and fast should change the page. This is working fine in my Pixel 2 (Android 10) and in Android the emulator. However, we tried a couple of devices where is not working correctly:
This is the behavior on for the vertical ViewPager:
Basically the user needs to scroll all the way down or up (manually) to trigger a change of page. With the horizontal ViewPager (recorded on the same phone), it works just as expected:
Would love to get some help from you @devvit if you know what might be causing this.