首页 > 解决方案 > ReactNative ScrollView onScroll 适用于 IOS 但不适用于 Android

问题描述

我试图通过使用onScroll函数使 Animated.ScollView 在Reactnative中表现得像一个 bottomSheet,在该函数中我得到 contentOffsetY 并基于此我使 scrollView 卸载/向下拖动(适用于 IOS)。但对于 Android,它不是触发 onScroll 的事件。

import * as React from 'react';
import { StyleSheet, Dimensions, Animated, View } from 'react-native';

const { width, height } = Dimensions.get('window');
import { Icon } from 'react-native-elements';
export const BottomSheet = ({
  indicatorMargin,
  heightFactor,
  children,
  gmRef,
  dragging = true,
}) => {
  const [alignment] = React.useState(new Animated.Value(height));
  const [onAnimate, setOnAnimate] = React.useState(false);
  const [isOpen, setIsOpen] = React.useState(false);
  const scrollRef = React.useRef();
  React.useEffect(
    () => dragSheetUp(),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    []
  );

  const dragSheetUp = React.useCallback(() => {
    setOnAnimate(true);
    setIsOpen(true);
    Animated.timing(alignment, {
      toValue: 0,
      duration: 500,
      useNativeDriver: false,
    }).start(() => setOnAnimate(false));
    scrollRef?.current?.scrollTo({
      y: 0,
      animated: true,
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const dragSheetDown = React.useCallback(() => {
    setOnAnimate(true);
    Animated.timing(alignment, {
      toValue: height,
      duration: 500,
      useNativeDriver: false,
    }).start(() => {
      setOnAnimate(false);
      setIsOpen(false);
    });

    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  React.useImperativeHandle(
    gmRef,
    () => ({
      open: () => dragSheetUp(),
      close: () => dragSheetDown(),
    }),
    [dragSheetDown, dragSheetUp]
  );

  const actionSheetBottomStyle = {
    top: alignment,
    marginTop: height * (heightFactor - 1),
  };

  const gestureHandler = (e) => {
    if (onAnimate) return;
    if (e.nativeEvent.contentOffset.y < -40 && dragging) {
      dragSheetDown();
    }
  };

  const calculatedHeight = height * (heightFactor - 1) - 100;

  return (
    <Animated.ScrollView
      ref={scrollRef}
      style={[styles.bottomSheetContainer, actionSheetBottomStyle]}
      scrollEventThrottle={12}
      showsVerticalScrollIndicator={false}
      onScroll={(e) => {
        gestureHandler(e);
      }}
    >
      {isOpen && dragging ? (
        <View
          style={styles.closeBtn}
          onStartShouldSetResponder={() => {
            dragSheetDown();
          }}
        >
          <Icon name="chevron-down" type="feather" color="black" size={28} />
        </View>
      ) : (
        <View
          style={[
            styles.indicator,
            {
              marginBottom: indicatorMargin ? indicatorMargin : 10,
            },
          ]}
        />
      )}

      {children}
      <View style={[styles.bottomSpace, { height: calculatedHeight }]} />
    </Animated.ScrollView>
  );
};

const styles = StyleSheet.create({
  bottomSheetContainer: {
    backgroundColor: '#fff',
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    height: height / 1.2,
    width: width / 1,
    borderTopRightRadius: 20,
    borderTopLeftRadius: 20,
    paddingVertical: 10,
  },
  indicator: {
    width: 30,
    borderTopColor: 'black',
    borderRadius: 15,
    borderTopWidth: 4,
    alignSelf: 'center',
  },
  bottomSpace: {
    backgroundColor: '#fff',
  },
  closeBtn: {
    width: 40,
    alignSelf: 'center',
  },
});

(注意:即使我们在 ScrollView 的顶部,我也想处理 onScroll,它适用于 IOS 但不适用于 android)

标签: react-nativereact-native-androidreact-native-ios

解决方案


推荐阅读