我目前正在尝试在 react-navigaion Material-top-tabs 的内容中定位水平 ScrollView (也水平滚动)。
在水平ScrollView内拖动时,只有ScrollView会受到影响并滚动。
有时在水平 ScrollView 内拖动时,整个顶部选项卡会滚动。 (选项卡正在切换)这对于用户体验来说是一场噩梦。
您知道有什么方法可以使其按预期方式工作吗?
代码片段:
Navigation.js
// Importing Top Tabs creator
import { createMaterialTopTabNavigator } from "@react-navigation/material-top-tabs";
...
// Creating the Tab Navigator
const Tab = createMaterialTopTabNavigator();
...
// Configure Navigator
<Tab.Navigator
initialRouteName="Tab 1"
screenOptions={{
headerShadowVisible: false,
headerStyle: {
backgroundColor: colors.background,
},
}}
// Using custom TabBar component
tabBar={(props) => <TabBar {...props} />}
>
// The horizontal ScrollView is in "Tab 1"
<Tab.Screen
name="Tab 1"
component={Screen1}
options={{
headerShown: false,
unmountOnBlur: true,
}}
/>
...
<Tab.Screen
name="Tab 4"
component={Screen4}
options={{
headerShown: false,
unmountOnBlur: true,
}}
/>
</Tab.Navigator>
HorizontalScrollView.js
<ScrollView
style={{
display: "flex",
flexDirection: "row",
backgroundColor: colors.background,
paddingHorizontal: 10,
}}
horizontal
showsHorizontalScrollIndicator={false}
overScrollMode="never"
>
...
</ScrollView>
我也为这个问题苦恼了几天。据我所知,这个问题并不特定于材料顶部选项卡或分页视图。如果将水平滚动视图或平面列表放入垂直滚动视图中,水平手势有时会被父垂直滚动视图窃取。
我注意到 onResponderTerminate 回调在水平滚动视图上被调用。 React Native 提供了一种通过传递 onResponderTerminationRequest={(event) => false} 属性来防止终止的方法,但这似乎没有做任何事情。终止回调仍然被调用。第一个错误报告已经有 6 年多了,我也没有找到任何有效的修复。
一个临时的解决方法是使用react-native-gesture-handler 和react-native-reanimated 构建自己的滚动视图。下面是如何制作的完整示例。请注意,Animated 必须从react-native-reanimated 导入。如果需要使用react-native中的Animated,请使用别名导入 --> import {Animated as Anim} from 'react-native';
import React from 'react';
import { Animated as Anim, ScrollView, Text, View } from 'react-native';
import Animated, {
useAnimatedStyle,
useSharedValue,
withDecay,
withSpring
} from "react-native-reanimated";
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
export const MyTab = () => {
// How many points the view is scrollable.
// In other words, how many points the view overflows.
// You might need to dynamically update this
// This can be calculated by subtracting screen width from the view width.
// If the view doesn't overflow, set this to zero.
const SCROLLABLE_WIDTH = 200;
const animatedX = useSharedValue(0);
const xContext = useSharedValue(0);
const animStyle = useAnimatedStyle(() => {
return {
transform: [{ translateX: animatedX.value }]
}
})
const panGesture = React.useMemo(() => Gesture.Pan()
.activeOffsetX([-17, 17]) // 17 is the optimal value. Anything higher might cause a tab change
.activeOffsetY([-22, 22]) // Allows the vertical scrollview to take over when swiping vertically
.maxPointers(1)
.onStart((e) => {
xContext.value = animatedX.value;
})
.onUpdate((e) => {
const target = xContext.value + e.translationX;
if (target > 0) {
animatedX.value = target * 0.3;
}
else if (target < -SCROLLABLE_WIDTH) {
animatedX.value = -SCROLLABLE_WIDTH + (SCROLLABLE_WIDTH + target) * 0.3;
}
else {
animatedX.value = target;
}
})
.onEnd((e) => {
const target = xContext.value + e.translationX;
if (target > 0) {
animatedX.value = withSpring(0, { mass: 0.3, stiffness: 110 });
}
else if (target < -SCROLLABLE_WIDTH) {
animatedX.value = withSpring(-SCROLLABLE_WIDTH, { mass: 0.3, stiffness: 110 });
}
else {
animatedX.value = withDecay({
velocity: e.velocityX,
clamp: [-SCROLLABLE_WIDTH, 0], // optionally define boundaries for the animation
});
}
}),
[] // Set here your useStates required in the gesture
);
return (
<ScrollView style={{}}>
<GestureDetector
gesture={panGesture}
>
{/* If no static container is set, a tab change might initialize
If you need to hide overflow set overflow: "hidden" to the container style */}
<Animated.View style={{ marginVertical: 50 }}>
<Animated.View
style={[{
flexDirection: "row"
},
animStyle
]}>
<Text style={{ color: "#abcdef", fontSize: 28, marginHorizontal: 20 }}>
{"Horizontally\nscrollable\nitem 1"}
</Text>
<Text style={{ color: "#abcdef", fontSize: 28, marginHorizontal: 20 }}>
{"Horizontally\nscrollable\nitem 2"}
</Text>
<Text style={{ color: "#abcdef", fontSize: 28, marginHorizontal: 20 }}>
{"Horizontally\nscrollable\nitem 3"}
</Text>
<Text style={{ color: "#abcdef", fontSize: 28, marginHorizontal: 20 }}>
{"Horizontally\nscrollable\nitem 4"}
</Text>
</Animated.View>
</Animated.View>
</GestureDetector>
</ScrollView>
)
}
对我有用的是使用从“react-native-gesture-handler”而不是“react-native”导入的 ScrollView 组件。
我不需要做任何额外的设置,它工作得很好。
import {ScrollView} from 'react-native-gesture-handler';