当Animated.View将Animated.ScrollView作为子级时,PanResponder在Android上无法正常工作

时间:2019-05-01 16:28:38

标签: javascript android react-native

我的手势在Android设备上有问题。

我的Animated.View内部有一个Animated.ScrollView。每当我缓慢拖动Animated.View时,它都能正常工作,但是只要我快速滑动一下,Animated.View就会在几个像素后停止。

当我禁用Animated.ScrollView上的滚动时,该错误不会发生。我的猜测是,如果滑动速度过快,则Animated.ScrollView会接手。

任何人都知道为什么会这样吗?在iOS上不会发生这种情况。

详细信息视图:

class DetailScreen extends PureComponent<Props> {
    state = {
        closeToTop: true,
        closeToBottom: false
    };

    scrollY = new Animated.Value(0);
    scrollView?: ScrollView;

    handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
        const closeToTop = this.isCloseToTop(e);
        if (closeToTop !== this.state.closeToTop) {
            this.setState({ closeToTop });
        }

        const closeToBottom = this.isCloseToBottom(e);
        if (closeToBottom !== this.state.closeToBottom) {
            this.setState({ closeToBottom });
        }
    };

    isCloseToTop = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
        const { contentOffset } = e.nativeEvent;

        return contentOffset.y === 0;
    };

    isCloseToBottom = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
        const { layoutMeasurement, contentOffset, contentSize } = e.nativeEvent;
        return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20;
    };

    renderContent = () => {
        return (
            <Animated.ScrollView
                contentContainerStyle={{ flexGrow: 1 }}
                scrollEventThrottle={16}
                bounces={false}
                // scrollEnabled={false}
                overScrollMode={"never"}
                onScroll={Animated.event(
                    [
                        {
                            nativeEvent: {
                                contentOffset: {
                                    y: this.scrollY
                                }
                            }
                        }
                    ],
                    {
                        useNativeDriver: true,
                        listener: this.handleScroll
                    }
                )}
                ref={(ref: any) => (this.scrollView = ref && ref.getNode())}>
                <TouchableWithoutFeedback>
                    <View style={{flex: 1}}>
                        <View style={{...styles.container, height: 200, backgroundColor: "red"}}>
                            <Text>red</Text>
                        </View>
                        <View style={{ ...styles.container, height: 200, backgroundColor: "yellow" }}>
                            <Text>yellow</Text>
                        </View>
                        <View style={{ ...styles.container, height: 200, backgroundColor: "green" }}>
                            <Text>green</Text>
                        </View>
                        <View style={{ ...styles.container, height: 200, backgroundColor: "blue" }}>
                            <Text>blue</Text>
                        </View>
                        <View style={{ ...styles.container, height: 200, backgroundColor: "red" }}>
                            <Text>red</Text>
                        </View>
                        <View style={{ ...styles.container, height: 200, backgroundColor: "yellow" }}>
                            <Text>yellow</Text>
                        </View>
                    </View>
                </TouchableWithoutFeedback>
            </Animated.ScrollView>
        );
    };

    render() {
        return (
                <SwipeableModal
                    onClose={this.props.navigation.pop}
                    nearTop={this.state.closeToTop}
                    nearBottom={this.state.closeToBottom}>
                    {this.renderContent()}
                </SwipeableModal>
        );
    }
 }

可滑动模式

const { height: screenHeight } = Dimensions.get("window");
const TIMING_CONFIG = { duration: 300, easing: Easing.inOut(Easing.ease) };

class SwipeableModal extends PureComponent<Props, State> {
    panResponder: PanResponderInstance;

    constructor(props: Props) {
        super(props);
        this.state = {
            pan: new Animated.ValueXY({ x: 0, y: 0 }),
            isDragging: false
        };

        this.panResponder = PanResponder.create({
            // Ask to be the responder:
            onStartShouldSetPanResponder: () => false,
            onStartShouldSetPanResponderCapture: () => false,
            onMoveShouldSetPanResponderCapture: () => false,
            onPanResponderTerminationRequest: () => false,

            onMoveShouldSetPanResponder: (evt, gestureState) => {
                if (this.state.isDragging) {
                    return true;
                }

                // moving finger from top to bottom
                if (gestureState.vy > 0 && this.props.nearTop) {
                    this.setState({ isDragging: true });
                    return true;
                }

                // moving finger from bottom to top
                if (gestureState.vy < 0 && this.props.nearBottom) {
                    this.setState({ isDragging: true });
                    return true;
                }

                return false;
            },
            onPanResponderMove: (evt, gestureState) => {
                this.state.pan.setValue({ x: 0, y: gestureState.dy });
            },
            onPanResponderRelease: (evt, gestureState) => {
                this.setState({ isDragging: false });
                if (gestureState.vy <= -0.7 || gestureState.dy <= -300) {
                    // move from bottom to top
                    Animated.timing(this.state.pan, {
                        toValue: { x: 0, y: -screenHeight },
                        ...TIMING_CONFIG
                    }).start(this.closeModal);
                } else if (gestureState.vy >= 0.5 || gestureState.dy >= 300) {
                    // move from top to
                    Animated.timing(this.state.pan, {
                        toValue: { x: 0, y: screenHeight },
                        ...TIMING_CONFIG
                    }).start(this.closeModal);
                } else {
                    Animated.spring(this.state.pan, {
                        toValue: 0
                    }).start();
                }
            }
        });
    }

    closeModal = () => {
        this.props.onClose();
    };

    handleGetStyle() {
        return [
            style.container,
            {
                transform: [...this.state.pan.getTranslateTransform()]
            }
        ];
    }

    render() {
        return (
            <Animated.View style={this.handleGetStyle()} {...this.panResponder.panHandlers}>
                {this.props.children}
            </Animated.View>
        );
    }
}

Video example Github project that reproduces the bug

1 个答案:

答案 0 :(得分:0)

对于那些想知道的人,我能够通过使用react-native-gesture-handler重新实现可滑动模式来解决此问题。

如果有人需要帮助,请告诉我。