react-native-swipe-list侧滑删除

岳炎彬
2023-12-01

最近写rn项目,有遇到类似购物车侧滑删除的功能,刚开始看到了antd里面的SwipeAction 滑动操作这个组件,适配单个侧滑使用效果,达不到预想的那种列表滑动下一个单元上一个合并的效果。然后百度了一方找到了react-native-swipe-list-view组件库,想操作一番。因项目的体制问题,我们不能随便下载插件,然后我去就官网看了下源码,复制粘贴吗,程序员必备技术,源码放到自己的项目中实现了一下,但是还是不够近仁义啊,不能直接使用啊,预期的效果还是不够,然而只能在基于改动它的源码了。如下:
SwipeRow 这个代码并没有实质性的改动

'use strict';

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  Dimensions,
  Animated,
  PanResponder,
  StyleSheet,
  TouchableOpacity,
  ViewPropTypes,
  View,
} from 'react-native';

const DEFAULT_PREVIEW_OPEN_DELAY = 700;
const PREVIEW_CLOSE_DELAY = 300;
const MAX_VELOCITY_CONTRIBUTION = 5;
const SCROLL_LOCK_MILLISECONDS = 300;

/**
 * Row that is generally used in a SwipeListView.
 * If you are rendering a SwipeRow explicitly you must pass the SwipeRow exactly two children.
 * The first will be rendered behind the second.
 * e.g.
 <SwipeRow>
 <View style={hiddenRowStyle} />
 <View style={visibleRowStyle} />
 </SwipeRow>
 */
class SwipeRow extends Component {
  constructor(props) {
    super(props);
    this.isOpen = false;
    this.previousTrackedTranslateX = 0;
    this.currentTranslateX = 0;
    this.previousTrackedDirection = null;
    this.horizontalSwipeGestureBegan = false;
    this.swipeInitialX = null;
    this.parentScrollEnabled = true;
    this.ranPreview = false;
    this._ensureScrollEnabledTimer = null;
    this.isForceClosing = false;
    this.state = {
      dimensionsSet: false,
      hiddenHeight: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
      hiddenWidth: this.props.disableHiddenLayoutCalculation ? '100%' : 0,
    };
    this._translateX = new Animated.Value(0);

    this._panResponder = PanResponder.create({
      onMoveShouldSetPanResponder: (e, gs) =>
        this.handleOnMoveShouldSetPanResponder(e, gs),
      onPanResponderMove: (e, gs) => this.handlePanResponderMove(e, gs),
      onPanResponderRelease: (e, gs) => this.handlePanResponderEnd(e, gs),
      onPanResponderTerminate: (e, gs) => this.handlePanResponderEnd(e, gs),
      onShouldBlockNativeResponder: () => false,
    });

    this._translateX.addListener(({ value }) => {
      this.currentTranslateX = value;
      if (this.props.onSwipeValueChange) {
        let direction = this.previousTrackedDirection;
        if (value !== this.previousTrackedTranslateX) {
          direction = value > this.previousTrackedTranslateX ? 'right' : 'left';
        }
        this.props.onSwipeValueChange &&
          this.props.onSwipeValueChange({
            isOpen: this.isOpen,
            direction,
            value,
          });
        this.previousTrackedTranslateX = value;
        this.previousTrackedDirection = direction;
      }
    });

    if (
      this.props.forceCloseToRightThreshold &&
      this.props.forceCloseToRightThreshold > 0
    ) {
      this._translateX.addListener(({ value }) => {
        if (
          !this.isForceClosing &&
          Dimensions.get('window').width + value <
            this.props.forceCloseToRightThreshold
        ) {
          this.isForceClosing = true;
          this.forceCloseRow('right');
          if (this.props.onForceCloseToRight) {
            this.props.onForceCloseToRight();
          }
        }
      });
    }

    if (
      this.props.forceCloseToLeftThreshold &&
      this.props.forceCloseToRightThreshold > 0
    ) {
      this._translateX.addListener(({ value }) => {
        if (
          !this.isForceClosing &&
          Dimensions.get('window').width - value <
            this.props.forceCloseToLeftThreshold
        ) {
          this.isForceClosing = true;
          this.forceCloseRow('left');
          if (this.props.onForceCloseToLeft) {
            this.props.onForceCloseToLeft();
          }
        }
      });
    }
  }

  componentWillUnmount() {
    clearTimeout(this._ensureScrollEnabledTimer);
    this._translateX.removeAllListeners();
  }

  shouldComponentUpdate(nextProps, nextState) {
    if (
      this.state.hiddenHeight !== nextState.hiddenHeight ||
      this.state.hiddenWidth !== nextState.hiddenWidth ||
      !this.props.shouldItemUpdate ||
      (this.props.shouldItemUpdate &&
        this.props.shouldItemUpdate(this.props.item, nextProps.item))
    ) {
      return true;
    }

    return false;
  }

  getPreviewAnimation(toValue, delay) {
    return Animated.timing(this._translateX, {
      duration: this.props.previewDuration,
      toValue,
      delay,
      useNativeDriver: this.props.useNativeDriver,
    });
  }

  onContentLayout(e) {
    this.setState({
      dimensionsSet: !this.props.recalculateHiddenLayout,
      ...(!this.props.disableHiddenLayoutCalculation
        ? {
            hiddenHeight: e.nativeEvent.layout.height,
            hiddenWidth: e.nativeEvent.layout.width,
          }
        : {}),
    });

    if (this.props.preview && !this.ranPreview) {
      this.ranPreview = true;
      const previewOpenValue =
        this.props.previewOpenValue || this.props.rightOpenValue * 0.5;
      this.getPreviewAnimation(
        previewOpenValue,
        this.props.previewOpenDelay,
      ).start(() => {
        this.getPreviewAnimation(0, PREVIEW_CLOSE_DELAY).start();
      });
    }
  }

  onRowPress() {
    if (this.props.onRowPress) {
      this.props.onRowPress();
    } else {
      if (this.props.closeOnRowPress) {
        this.closeRow();
      }
    }
  }

  handleOnMoveShouldSetPanResponder(e, gs) {
    const { dx } = gs;
    return Math.abs(dx) > this.props.directionalDistanceChangeThreshold;
  }

  handlePanResponderMove(e, gestureState) {
    /* If the view is force closing, then ignore Moves. Return */
    if (this.isForceClosing) {
      return;
    }

    /* Else, do normal job */
    const { dx, dy } = gestureState;
    const absDx = Math.abs(dx);
    const absDy = Math.abs(dy);

    // this check may not be necessary because we don't capture the move until we pass the threshold
    // just being extra safe here
    if (
      absDx > this.props.directionalDistanceChangeThreshold ||
      absDy > this.props.directionalDistanceChangeThreshold
    ) {
      // we have enough to determine direction
      if (absDy > absDx && !this.horizontalSwipeGestureBegan) {
        // user is moving vertically, do nothing, listView will handle
        return;
      }

      // user is moving horizontally
      if (this.parentScrollEnabled) {
        // disable scrolling on the listView parent
        this.parentScrollEnabled = false;
        this.props.setScrollEnabled && this.props.setScrollEnabled(false);
      }

      if (this.swipeInitialX === null) {
        // set tranlateX value when user started swiping
        this.swipeInitialX = this.currentTranslateX;
      }
      if (!this.horizontalSwipeGestureBegan) {
        this.horizontalSwipeGestureBegan = true;
        this.props.swipeGestureBegan && this.props.swipeGestureBegan();
      }

      let newDX = this.swipeInitialX + dx;
      if (this.props.disableLeftSwipe && newDX < 0) {
        newDX = 0;
      }
      if (this.props.disableRightSwipe && newDX > 0) {
        newDX = 0;
      }

      if (this.props.stopLeftSwipe && newDX > this.props.stopLeftSwipe) {
        newDX = this.props.stopLeftSwipe;
      }
      if (this.props.stopRightSwipe && newDX < this.props.stopRightSwipe) {
        newDX = this.props.stopRightSwipe;
      }

      this._translateX.setValue(newDX);
    }
  }

  ensureScrollEnabled = () => {
    if (!this.parentScrollEnabled) {
      this.parentScrollEnabled = true;
      this.props.setScrollEnabled && this.props.setScrollEnabled(true);
    }
  };

  handlePanResponderEnd(e, gestureState) {
    /* PandEnd will reset the force-closing state when it's true. */
    if (this.isForceClosing) {
      this.isForceClosing = false;
    }
    // decide how much the velocity will affect the final position that the list item settles in.
    const swipeToOpenVelocityContribution = this.props
      .swipeToOpenVelocityContribution;
    const possibleExtraPixels =
      this.props.rightOpenValue * swipeToOpenVelocityContribution;
    const clampedVelocity = Math.min(
      gestureState.vx,
      MAX_VELOCITY_CONTRIBUTION,
    );
    const projectedExtraPixels =
      possibleExtraPixels * (clampedVelocity / MAX_VELOCITY_CONTRIBUTION);

    // re-enable scrolling on listView parent
    this._ensureScrollEnabledTimer = setTimeout(
      this.ensureScrollEnabled,
      SCROLL_LOCK_MILLISECONDS,
    );

    // finish up the animation
    let toValue = 0;
    if (this.currentTranslateX >= 0) {
      // trying to swipe right
      if (this.swipeInitialX < this.currentTranslateX) {
        if (
          this.currentTranslateX - projectedExtraPixels >
          this.props.leftOpenValue * (this.props.swipeToOpenPercent / 100)
        ) {
          // we're more than halfway
          toValue = this.props.leftOpenValue;
        }
      } else {
        if (
          this.currentTranslateX - projectedExtraPixels >
          this.props.leftOpenValue * (1 - this.props.swipeToClosePercent / 100)
        ) {
          toValue = this.props.leftOpenValue;
        }
      }
    } else {
      // trying to swipe left
      if (this.swipeInitialX > this.currentTranslateX) {
        if (
          this.currentTranslateX - projectedExtraPixels <
          this.props.rightOpenValue * (this.props.swipeToOpenPercent / 100)
        ) {
          // we're more than halfway
          toValue = this.props.rightOpenValue;
        }
      } else {
        if (
          this.currentTranslateX - projectedExtraPixels <
          this.props.rightOpenValue * (1 - this.props.swipeToClosePercent / 100)
        ) {
          toValue = this.props.rightOpenValue;
        }
      }
    }

    this.manuallySwipeRow(toValue);
  }

  /*
   * This method is called by SwipeListView
   */
  closeRow() {
    this.manuallySwipeRow(0);
  }

  /**
   * Force close the row toward the end of the given direction.
   * @param  {String} direction The direction to force close.
   */
  forceCloseRow(direction) {
    this.manuallySwipeRow(0, () => {
      if (direction === 'right' && this.props.onForceCloseToRightEnd) {
        this.props.onForceCloseToRightEnd();
      } else if (direction === 'left' && this.props.onForceCloseToLeftEnd) {
        this.props.onForceCloseToLeftEnd();
      }
    });
  }

  closeRowWithoutAnimation() {
    this._translateX.setValue(0);

    this.ensureScrollEnabled();
    this.isOpen = false;
    this.props.onRowDidClose && this.props.onRowDidClose();

    this.props.onRowClose && this.props.onRowClose();

    this.swipeInitialX = null;
    this.horizontalSwipeGestureBegan = false;
  }

  manuallySwipeRow(toValue, onAnimationEnd) {
    Animated.spring(this._translateX, {
      toValue,
      friction: this.props.friction,
      tension: this.props.tension,
      useNativeDriver: this.props.useNativeDriver,
    }).start(() => {
      this.ensureScrollEnabled();
      if (toValue === 0) {
        this.isOpen = false;
        this.props.onRowDidClose && this.props.onRowDidClose();
      } else {
        this.isOpen = true;
        this.props.onRowDidOpen && this.props.onRowDidOpen(toValue);
      }
      if (onAnimationEnd) {
        onAnimationEnd();
      }
    });

    if (toValue === 0) {
      this.props.onRowClose && this.props.onRowClose();
    } else {
      this.props.onRowOpen && this.props.onRowOpen(toValue);
    }

    // reset everything
    this.swipeInitialX = null;
    this.horizontalSwipeGestureBegan = false;
  }

  combinedOnPress = () => {
    const onPress = this.props.children[1].props.onPress;
    this.onRowPress();
    onPress && onPress();
  };

  renderVisibleContent() {
    if (!this.props.closeOnRowPress) {
      return this.props.children[1];
    }

    // handle touchables
    const onPress = this.props.children[1].props.onPress;

    if (onPress) {
      return React.cloneElement(this.props.children[1], {
        ...this.props.children[1].props,
        onPress: this.combinedOnPress,
      });
    }

    return (
      <TouchableOpacity activeOpacity={1} onPress={this.combinedOnPress}>
        {this.props.children[1]}
      </TouchableOpacity>
    );
  }

  renderRowContent() {
    // We do this annoying if statement for performance.
    // We don't want the onLayout func to run after it runs once.
    if (this.state.dimensionsSet) {
      return (
        <Animated.View
          manipulationModes={['translateX']}
          {...this._panResponder.panHandlers}
          style={{
            zIndex: 2,
            transform: [{ translateX: this._translateX }],
          }}>
          {this.renderVisibleContent()}
        </Animated.View>
      );
    } else {
      return (
        <Animated.View
          manipulationModes={['translateX']}
          {...this._panResponder.panHandlers}
          onLayout={e => this.onContentLayout(e)}
          style={{
            zIndex: 2,
            transform: [{ translateX: this._translateX }],
          }}>
          {this.renderVisibleContent()}
        </Animated.View>
      );
    }
  }

  render() {
    return (
      <View style={this.props.style ? this.props.style : styles.container}>
        <View
          style={[
            styles.hidden,
            {
              height: this.state.hiddenHeight,
              width: this.state.hiddenWidth,
            },
          ]}>
          {this.props.children[0]}
        </View>
        {this.renderRowContent()}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    // As of RN 0.29 flex: 1 is causing all rows to be the same height
    // flex: 1
  },
  hidden: {
    zIndex: 1,
    bottom: 0,
    left: 0,
    overflow: 'hidden',
    position: 'absolute',
    right: 0,
    top: 0,
  },
});

SwipeRow.propTypes = {
  /**
   * Used by the SwipeListView to close rows on scroll events.
   * You shouldn't need to use this prop explicitly.
   */
  setScrollEnabled: PropTypes.func,
  /**
   * Called when it has been detected that a row should be swiped open.
   */
  swipeGestureBegan: PropTypes.func,
  /**
   * Called when a swipe row is animating open. Used by the SwipeListView
   * to keep references to open rows.
   */
  onRowOpen: PropTypes.func,
  /**
   * Called when a swipe row has animated open.
   */
  onRowDidOpen: PropTypes.func,
  /**
   * TranslateX value for opening the row to the left (positive number)
   */
  leftOpenValue: PropTypes.number,
  /**
   * TranslateX value for opening the row to the right (negative number)
   */
  rightOpenValue: PropTypes.number,
  /**
   * TranslateX value for stop the row to the left (positive number)
   */
  stopLeftSwipe: PropTypes.number,
  /**
   * TranslateX value for stop the row to the right (negative number)
   */
  stopRightSwipe: PropTypes.number,
  /**
   * Friction for the open / close animation
   */
  friction: PropTypes.number,
  /**
   * Tension for the open / close animation
   */
  tension: PropTypes.number,
  /**
   * Should the row be closed when it is tapped
   */
  closeOnRowPress: PropTypes.bool,
  /**
   * Disable ability to swipe the row left
   */
  disableLeftSwipe: PropTypes.bool,
  /**
   * Disable ability to swipe the row right
   */
  disableRightSwipe: PropTypes.bool,
  /**
   * Enable hidden row onLayout calculations to run always
   */
  recalculateHiddenLayout: PropTypes.bool,
  /**
   * Disable hidden row onLayout calculations
   */
  disableHiddenLayoutCalculation: PropTypes.bool,
  /**
   * Called when a swipe row is animating closed
   */
  onRowClose: PropTypes.func,
  /**
   * Called when a swipe row has animated closed
   */
  onRowDidClose: PropTypes.func,
  /**
   * Styles for the parent wrapper View of the SwipeRow
   */
  style: ViewPropTypes.style,
  /**
   * Should the row do a slide out preview to show that it is swipeable
   */
  preview: PropTypes.bool,
  /**
   * Duration of the slide out preview animation
   */
  previewDuration: PropTypes.number,
  /**
   * TranslateX value for the slide out preview animation
   * Default: 0.5 * props.rightOpenValue
   */
  previewOpenValue: PropTypes.number,
  /**
   * The dx value used to detect when a user has begun a swipe gesture
   */
  directionalDistanceChangeThreshold: PropTypes.number,
  /**
   * What % of the left/right openValue does the user need to swipe
   * past to trigger the row opening.
   */
  swipeToOpenPercent: PropTypes.number,
  /**
   * Describes how much the ending velocity of the gesture contributes to whether the swipe will result in the item being closed or open.
   * A velocity factor of 0 means that the velocity will have no bearing on whether the swipe settles on a closed or open position
   * and it'll just take into consideration the swipeToOpenPercent.
   */
  swipeToOpenVelocityContribution: PropTypes.number,
  /**
   * What % of the left/right openValue does the user need to swipe
   * past to trigger the row closing.
   */
  swipeToClosePercent: PropTypes.number,
  /**
   * callback to determine whether component should update (currentItem, newItem)
   */
  shouldItemUpdate: PropTypes.func,
  /**
   * Callback invoked any time the swipe value of the row is changed
   */
  onSwipeValueChange: PropTypes.func,
  /**
   * TranslateX amount(not value!) threshold that triggers force-closing the row to the Left End (positive number)
   */
  forceCloseToLeftThreshold: PropTypes.number,
  /**
   * TranslateX amount(not value!) threshold that triggers force-closing the row to the Right End (positive number)
   */
  forceCloseToRightThreshold: PropTypes.number,
  /**
   * Callback invoked when row is force closing to the Left End
   */
  onForceCloseToLeft: PropTypes.func,
  /**
   * Callback invoked when row is force closing to the Right End
   */
  onForceCloseToRight: PropTypes.func,
  /**
   * Callback invoked when row has finished force closing to the Left End
   */
  onForceCloseToLeftEnd: PropTypes.func,
  /**
   * Callback invoked when row has finished force closing to the Right End
   */
  onForceCloseToRightEnd: PropTypes.func,
  /**
   * useNativeDriver: true for all animations where possible
   */
  useNativeDriver: PropTypes.bool,
  /**
   * Children
   */
  children: PropTypes.node.isRequired,
};

SwipeRow.defaultProps = {
  leftOpenValue: 0,
  rightOpenValue: 0,
  closeOnRowPress: true,
  disableLeftSwipe: false,
  disableRightSwipe: false,
  recalculateHiddenLayout: false,
  disableHiddenLayoutCalculation: false,
  preview: false,
  previewDuration: 300,
  previewOpenDelay: DEFAULT_PREVIEW_OPEN_DELAY,
  directionalDistanceChangeThreshold: 2,
  swipeToOpenPercent: 50,
  swipeToOpenVelocityContribution: 0,
  swipeToClosePercent: 50,
  item: {},
  useNativeDriver: true,
};

export default SwipeRow;

SwipeList 主要改动的是list这个库

'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import { FlatList, SectionList, Platform, ViewPropTypes } from 'react-native';
import BottomHint from '@components/PareFlatList/BottomHint';
import SwipeRow from './SwipeRow';

/**
 * ListView that renders SwipeRows.
 */
class SwipeList extends React.Component {
  constructor(props) {
    super(props);
    this._rows = {};
    this.openCellKey = null;
    this.listViewProps = {};
    if (Platform.OS === 'ios') {
      // Keep track of scroll offset and layout changes on iOS to be able to handle
      // https://github.com/jemise111/react-native-swipe-list-view/issues/109
      this.yScrollOffset = 0;
      this.layoutHeight = 0;
      this.listViewProps = {
        onLayout: e => this.onLayout(e),
        onContentSizeChange: (w, h) => this.onContentSizeChange(w, h),
      };
    }
  }

  setScrollEnabled(enable) {
    if (this.props.scrollEnabled === false) {
      return;
    }
    // Due to multiple issues reported across different versions of RN
    // We do this in the safest way possible...
    if (this._listView && this._listView.setNativeProps) {
      this._listView.setNativeProps({ scrollEnabled: enable });
    } else if (this._listView && this._listView.getScrollResponder) {
      const scrollResponder = this._listView.getScrollResponder();
      scrollResponder.setNativeProps &&
        scrollResponder.setNativeProps({ scrollEnabled: enable });
    }
    this.props.onScrollEnabled && this.props.onScrollEnabled(enable);
  }

  safeCloseOpenRow() {
    const rowRef = this._rows[this.openCellKey];
    if (rowRef && rowRef.closeRow) {
      this._rows[this.openCellKey].closeRow();
    }
  }

  rowSwipeGestureBegan(key) {
    if (
      this.props.closeOnRowBeginSwipe &&
      this.openCellKey &&
      this.openCellKey !== key
    ) {
      this.safeCloseOpenRow();
    }

    if (this.props.swipeGestureBegan) {
      this.props.swipeGestureBegan(key);
    }
  }

  onRowOpen(key, toValue) {
    if (
      this.openCellKey &&
      this.openCellKey !== key &&
      this.props.closeOnRowOpen &&
      !this.props.closeOnRowBeginSwipe
    ) {
      this.safeCloseOpenRow();
    }
    this.openCellKey = key;
    this.props.onRowOpen && this.props.onRowOpen(key, this._rows, toValue);
  }

  onRowPress() {
    if (this.openCellKey) {
      if (this.props.closeOnRowPress) {
        this.safeCloseOpenRow();
        this.openCellKey = null;
      }
    }
  }

  onScroll(e) {
    if (Platform.OS === 'ios') {
      this.yScrollOffset = e.nativeEvent.contentOffset.y;
    }
    if (this.openCellKey) {
      if (this.props.closeOnScroll) {
        this.safeCloseOpenRow();
        this.openCellKey = null;
      }
    }
    this.props.onScroll && this.props.onScroll(e);
  }

  onLayout(e) {
    this.layoutHeight = e.nativeEvent.layout.height;
    this.props.onLayout && this.props.onLayout(e);
  }

  // When deleting rows on iOS, the list may end up being over-scrolled,
  // which will prevent swiping any of the remaining rows. This triggers a scrollToEnd
  // when that happens, which will make sure the list is kept in bounds.
  // See: https://github.com/jemise111/react-native-swipe-list-view/issues/109
  onContentSizeChange(w, h) {
    const height = h - this.layoutHeight;
    if (this.yScrollOffset >= height && height > 0) {
      if (this._listView instanceof FlatList) {
        this._listView && this._listView.scrollToEnd();
      }
    }
    this.props.onContentSizeChange && this.props.onContentSizeChange(w, h);
  }

  setRefs(ref) {
    this._listView = ref;
    this.props.listViewRef && this.props.listViewRef(ref);
  }

  // In most use cases this is no longer used. Only in the case we are passed renderListView={true}
  // there may be legacy code that passes a this.props.renderRow func so we keep this for legacy purposes
  renderRow(rowData, secId, rowId, rowMap) {
    const key = `${secId}${rowId}`;
    const Component = this.props.renderRow(rowData, secId, rowId, rowMap);
    const HiddenComponent =
      this.props.renderHiddenRow &&
      this.props.renderHiddenRow(rowData, secId, rowId, rowMap);
    const previewRowId =
      this.props.dataSource &&
      this.props.dataSource.getRowIDForFlatIndex(
        this.props.previewRowIndex || 0,
      );
    const shouldPreviewRow =
      (this.props.previewFirstRow || this.props.previewRowIndex) &&
      rowId === previewRowId;
    return this.renderCell(
      Component,
      HiddenComponent,
      key,
      rowData,
      shouldPreviewRow,
    );
  }

  renderItem(rowData, rowMap) {
    const Component = this.props.renderItem(rowData, rowMap);
    const HiddenComponent =
      this.props.renderHiddenItem &&
      this.props.renderHiddenItem(rowData, rowMap);
    const { item, index } = rowData;
    let { num } = item;
    let key = Number(num);
    console.log(key);
    if (!key && this.props.keyExtractor) {
      key = this.props.keyExtractor(item, index);
    }

    const shouldPreviewRow =
      typeof key !== 'undefined' && this.props.previewRowKey === key;
    return this.renderCell(
      Component,
      HiddenComponent,
      key,
      item,
      shouldPreviewRow,
    );
  }

  renderCell(VisibleComponent, HiddenComponent, key, item, shouldPreviewRow) {
    if (!HiddenComponent) {
      return React.cloneElement(VisibleComponent, {
        ...VisibleComponent.props,
        ref: row => (this._rows[key] = row),
        onRowOpen: toValue => this.onRowOpen(key, toValue),
        onRowDidOpen: toValue =>
          this.props.onRowDidOpen &&
          this.props.onRowDidOpen(key, this._rows, toValue),
        onRowClose: () =>
          this.props.onRowClose && this.props.onRowClose(key, this._rows),
        onRowDidClose: () =>
          this.props.onRowDidClose && this.props.onRowDidClose(key, this._rows),
        onRowPress: () => this.onRowPress(),
        setScrollEnabled: enable => this.setScrollEnabled(enable),
        swipeGestureBegan: () => this.rowSwipeGestureBegan(key),
      });
    } else {
      return (
        <SwipeRow
          onSwipeValueChange={
            this.props.onSwipeValueChange
              ? data =>
                  this.props.onSwipeValueChange({
                    ...data,
                    key,
                  })
              : null
          }
          ref={row => (this._rows[key] = row)}
          swipeGestureBegan={() => this.rowSwipeGestureBegan(key)}
          onRowOpen={toValue => this.onRowOpen(key, toValue)}
          onRowDidOpen={toValue =>
            this.props.onRowDidOpen &&
            this.props.onRowDidOpen(key, this._rows, toValue)
          }
          onRowClose={() =>
            this.props.onRowClose && this.props.onRowClose(key, this._rows)
          }
          onRowDidClose={() =>
            this.props.onRowDidClose &&
            this.props.onRowDidClose(key, this._rows)
          }
          onRowPress={() => this.onRowPress(key)}
          shouldItemUpdate={
            this.props.shouldItemUpdate
              ? (currentItem, newItem) =>
                  this.props.shouldItemUpdate(currentItem, newItem)
              : null
          }
          setScrollEnabled={enable => this.setScrollEnabled(enable)}
          leftOpenValue={item.leftOpenValue || this.props.leftOpenValue}
          rightOpenValue={item.rightOpenValue || this.props.rightOpenValue}
          closeOnRowPress={item.closeOnRowPress || this.props.closeOnRowPress}
          disableLeftSwipe={
            item.disableLeftSwipe || this.props.disableLeftSwipe
          }
          disableRightSwipe={
            item.disableRightSwipe || this.props.disableRightSwipe
          }
          stopLeftSwipe={item.stopLeftSwipe || this.props.stopLeftSwipe}
          stopRightSwipe={item.stopRightSwipe || this.props.stopRightSwipe}
          recalculateHiddenLayout={this.props.recalculateHiddenLayout}
          disableHiddenLayoutCalculation={
            this.props.disableHiddenLayoutCalculation
          }
          style={this.props.swipeRowStyle}
          preview={shouldPreviewRow}
          previewDuration={this.props.previewDuration}
          previewOpenDelay={this.props.previewOpenDelay}
          previewOpenValue={this.props.previewOpenValue}
          tension={this.props.tension}
          friction={this.props.friction}
          directionalDistanceChangeThreshold={
            this.props.directionalDistanceChangeThreshold
          }
          swipeToOpenPercent={this.props.swipeToOpenPercent}
          swipeToOpenVelocityContribution={
            this.props.swipeToOpenVelocityContribution
          }
          swipeToClosePercent={this.props.swipeToClosePercent}
          item={item} // used for should item update comparisons
          useNativeDriver={this.props.useNativeDriver}>
          {HiddenComponent}
          {VisibleComponent}
        </SwipeRow>
      );
    }
  }
  /**底部View */
  _footerComponent() {
    if (this.props.ListFooterComponent) {
      // return ListFooterComponent();
      console.log('22222');
    } else {
      return this.props.isHiddenFoot ? null : (
        <BottomHint
          showFoot={this.props.showFoot}
          textValue={this.props.bottomLineText}
        />
      );
    }
  }

  render() {
    const { useSectionList, renderListView, ...props } = this.props;
    // console.log(this.props, '-------')
    if (renderListView) {
      // Ideally renderRow should be deprecated. We do this check for
      // legacy purposes to not break certain renderListView cases
      const useRenderRow = !!this.props.renderRow;
      return renderListView(
        props,
        this.setRefs.bind(this),
        this.onScroll.bind(this),
        useRenderRow
          ? this.renderRow.bind(this, this._rows)
          : this.renderItem.bind(this),
      );
    }

    if (useSectionList) {
      return (
        <SectionList
          {...props}
          {...this.listViewProps}
          ref={c => this.setRefs(c)}
          onScroll={e => this.onScroll(e)}
          renderItem={rowData => this.renderItem(rowData, this._rows)}
          refreshControl={this.props.swipeRefreshControl || null}
          renderSectionHeader={this.props.renderHeaderView || null}
          stickySectionHeade
          rsEnabled={this.props.stickyHeadersEnabled || false}
        />
      );
    }
    return (
      <FlatList
        {...props}
        {...this.listViewProps}
        ref={c => this.setRefs(c)}
        onEndReachedThreshold={0.2}
        onScroll={e => this.onScroll(e)}
        renderItem={rowData => this.renderItem(rowData, this._rows)}
        refreshControl={this.props.swipeRefreshControl || null}
        ListFooterComponent={this._footerComponent(this.props)}
      />
    );
  }
}

SwipeListView.propTypes = {
  /**
   * To render a custom ListView component, if you don't want to use ReactNative one.
   * Note: This will call `renderRow`, not `renderItem`
   */
  renderListView: PropTypes.func,
  /**
   * How to render a row in a FlatList. Should return a valid React Element.
   */
  renderItem: PropTypes.func,
  /**
   * How to render a hidden row in a FlatList (renders behind the row). Should return a valid React Element.
   * This is required unless renderItem is passing a SwipeRow.
   */
  renderHiddenItem: PropTypes.func,
  /**
   * TranslateX value for opening the row to the left (positive number)
   */
  leftOpenValue: PropTypes.number,
  /**
   * TranslateX value for opening the row to the right (negative number)
   */
  rightOpenValue: PropTypes.number,
  /**
   * TranslateX value for stop the row to the left (positive number)
   */
  stopLeftSwipe: PropTypes.number,
  /**
   * TranslateX value for stop the row to the right (negative number)
   */
  stopRightSwipe: PropTypes.number,
  /**
   * Should open rows be closed when the listView begins scrolling
   */
  closeOnScroll: PropTypes.bool,
  /**
   * Should open rows be closed when a row is pressed
   */
  closeOnRowPress: PropTypes.bool,
  /**
   * Should open rows be closed when a row begins to swipe open
   */
  closeOnRowBeginSwipe: PropTypes.bool,
  /**
   * Should open rows be closed when another row is opened
   */
  closeOnRowOpen: PropTypes.bool,
  /**
   * Disable ability to swipe rows left
   */
  disableLeftSwipe: PropTypes.bool,
  /**
   * Disable ability to swipe rows right
   */
  disableRightSwipe: PropTypes.bool,
  /**
   * Enable hidden row onLayout calculations to run always.
   *
   * By default, hidden row size calculations are only done on the first onLayout event
   * for performance reasons.
   * Passing ```true```here will cause calculations to run on every onLayout event.
   * You may want to do this if your rows' sizes can change.
   * One case is a SwipeListView with rows of different heights and an options to delete rows.
   */
  recalculateHiddenLayout: PropTypes.bool,
  /**
   * Disable hidden row onLayout calculations
   *
   * Instead, {width: '100%', height: '100%'} will be used.
   * Improves performance by avoiding component updates, while still working with orientation changes.
   */
  disableHiddenLayoutCalculation: PropTypes.bool,
  /**
   * Called when a swipe row is animating swipe
   */
  swipeGestureBegan: PropTypes.func,
  /**
   * Called when a swipe row is animating open
   */
  onRowOpen: PropTypes.func,
  /**
   * Called when a swipe row has animated open
   */
  onRowDidOpen: PropTypes.func,
  /**
   * Called when a swipe row is animating closed
   */
  onRowClose: PropTypes.func,
  /**
   * Called when a swipe row has animated closed
   */
  onRowDidClose: PropTypes.func,
  /**
   * Called when scrolling on the SwipeListView has been enabled/disabled
   */
  onScrollEnabled: PropTypes.func,
  /**
   * Styles for the parent wrapper View of the SwipeRow
   */
  swipeRowStyle: ViewPropTypes.style,
  /**
   * Called when the FlatList ref is set and passes a ref to the FlatList
   * e.g. listViewRef={ ref => this._swipeListViewRef = ref }
   */
  listViewRef: PropTypes.func,
  /**
   * Should the row with this key do a slide out preview to show that the list is swipeable
   */
  previewRowKey: PropTypes.string,
  /**
   * [DEPRECATED] Should the first SwipeRow do a slide out preview to show that the list is swipeable
   */
  previewFirstRow: PropTypes.bool,
  /**
   * [DEPRECATED] Should the specified rowId do a slide out preview to show that the list is swipeable
   * Note: This ID will be passed to this function to get the correct row index
   * https://facebook.github.io/react-native/docs/listviewdatasource.html#getrowidforflatindex
   */
  previewRowIndex: PropTypes.number,
  /**
   * Duration of the slide out preview animation (milliseconds)
   */
  previewDuration: PropTypes.number,
  /**
   * Delay of the slide out preview animation (milliseconds) // default 700ms
   */
  prewiewOpenDelay: PropTypes.number,
  /**
   * TranslateX value for the slide out preview animation
   * Default: 0.5 * props.rightOpenValue
   */
  previewOpenValue: PropTypes.number,
  /**
   * Friction for the open / close animation
   */
  friction: PropTypes.number,
  /**
   * Tension for the open / close animation
   */
  tension: PropTypes.number,
  /**
   * The dx value used to detect when a user has begun a swipe gesture
   */
  directionalDistanceChangeThreshold: PropTypes.number,
  /**
   * What % of the left/right openValue does the user need to swipe
   * past to trigger the row opening.
   */
  swipeToOpenPercent: PropTypes.number,
  /**
   * Describes how much the ending velocity of the gesture affects whether the swipe will result in the item being closed or open.
   * A velocity factor of 0 means that the velocity will have no bearing on whether the swipe settles on a closed or open position
   * and it'll just take into consideration the swipeToOpenPercent.
   */
  swipeToOpenVelocityContribution: PropTypes.number,
  /**
   * What % of the left/right openValue does the user need to swipe
   * past to trigger the row closing.
   */
  swipeToClosePercent: PropTypes.number,
  /**
   * callback to determine whether component should update (currentItem, newItem)
   */
  shouldItemUpdate: PropTypes.func,
  /**
   * Callback invoked any time the swipe value of a SwipeRow is changed
   */
  onSwipeValueChange: PropTypes.func,
  /**
   * useNativeDriver: true for all animations where possible
   */
  useNativeDriver: PropTypes.bool,

  /**
   * 下拉组件
   */
  swipeRefreshControl: PropTypes.element,

  listHeaderView: PropTypes.func,
  showFoot: PropTypes.number,
  ListFooterComponent: PropTypes.element,

  // renderHeaderView: PropTypes.func,

  // stickyHeadersEnabled: PropTypes.bool
};

SwipeListView.defaultProps = {
  leftOpenValue: 0,
  rightOpenValue: 0,
  closeOnRowBeginSwipe: false,
  closeOnScroll: true,
  closeOnRowPress: true,
  closeOnRowOpen: true,
  disableLeftSwipe: false,
  disableRightSwipe: false,
  recalculateHiddenLayout: false,
  disableHiddenLayoutCalculation: false,
  previewFirstRow: false,
  directionalDistanceChangeThreshold: 2,
  swipeToOpenPercent: 50,
  swipeToOpenVelocityContribution: 0,
  swipeToClosePercent: 50,
  useNativeDriver: true,
  showFoot: 0,
};

export default SwipeList;

使用方法

 <SwipeListView
            style={[styles.scrollView, { marginBottom: marginButtom }]}
            data={landRelated.notesList}
            renderItem={(data, index) => (
              <Card
                key={`land-${data.index}`}
                data={data.item}
                onPress={() => props.onPress(data.item, data.index, 1)}
                checkoutFlag={checkoutFlag}
                onHandleEdit={onLandPress.bind(this, data.item)}
              />
            )}
            renderHiddenItem={(data, rowMap) =>
              checkoutFlag ? null : (
                <View style={styles.hideBgView}>
                  <TouchableOpacity
                    style={[styles.standaloneRowBack]}
                    onPress={() => checkDelete(data.item, this)}>
                    <View style={styles.swipe_cont_text_view}>
                      <Text style={styles.swipe_cont_text}>删除</Text>
                    </View>
                  </TouchableOpacity>
                </View>
              )
            }
            rightOpenValue={-75}
            disableRightSwipe={true}
            disableLeftSwipe={checkoutFlag ? true : false}
            refreshing={landRelated.refReshing}
            onRefresh={onRefresh}
            onEndReached={onEndReached}
            showFoot={landRelated.showFoot}
          />

大概就是这样!!!!!
如果侵犯请告知,会立马删除!!!谢谢

 类似资料: