如何仅在特定条件下实现自定义 BackHandler 行为?

How to achieve custom BackHandler behavior only under a certain condition?

我的应用程序中有一个平面列表,我想让用户在长按时将特定列表项标记为已选中,并提供一个删除按钮以一次性删除多个项目。这些是我期望的行为。


  1. 如果没有选择平面列表中的任何项目,按一个项目会打开一个包含项目详细信息的新屏幕,然后按“后退”按钮可返回平面列表。
  2. 如果没有选择任何项目,长按一个项目会将其标记为已选择。选择任何特定项目后按下的每个项目都被标记为已选择,而不是打开详细信息屏幕。
  3. 已经选中然后按下的项目变为未选中。
  4. 如果选择了任意数量的项目,将呈现一个删除按钮并且按下后退按钮取消选择所有项目。

我已经能够实现前三个行为中的大部分,但我完全迷失了 Back Handler。这是我的组件,为了简洁起见,只有相关代码。仅显示包含选择删除的项目的状态数组和用作平面列表的 RenderItem 道具的 listItem。

const Home = (props) => {
    const [deleteItems, setDeleteItems] = useState([]);
    const renderItem = ({ item }) => {
    let bb_OR_ub = item.useBy ? 'Use By ' : 'Best Before '
    let exp_check = CheckExp(item, 1);
    let listStyle = {};
    if (exp_check === -1)
      listStyle = { backgroundColor: '#ff9ea5' }
    else if (exp_check === 0)
      listStyle = { backgroundColor: '#fff185' }
    if (deleteItems.indexOf(item.name) != -1) {
      listStyle = { opacity: 0.3 }
    }
    return (
      <ListItem
        containerStyle={listStyle}
        badge={
          exp_check !== 1 ?
            exp_check === -1 ? { status: 'error', value: `!` } : {
              status: 'warning'
            } : null
        }
        title={item.name}
        subtitle={bb_OR_ub + item.date}
        bottomDivider
        leftAvatar={{ source: require('../shared/cexp.png'), imageProps: { resizeMode: 'contain' } }}
        onPress={() => {
          if (deleteItems.length == 0)
            navigate('ExpiryDetails', { item })
          else {
            setDeleteItems([...deleteItems, item.name])
          }
        }}
        onLongPress={() => {
          if (deleteItems.indexOf(item.name) == -1 || deleteItems.length == 0) {
            setDeleteItems([...deleteItems, item.name])
          }
          else {
            setDeleteItems(deleteItems.filter(el => el != item.name))
          }
        }} />
    );
  }

react-native 提供的 BackHandler 允许您订阅按下后退按钮。提供给后处理程序的回调可以提供 true(当不应触发默认行为时)或 false(当允许默认行为继续时)。

对于您的情况,我们希望在选择项目时在背面有自定义行为,此时我们希望取消选择所有项目。

我调整了您的代码以引入 BackHandler 并取消选择背面被按下的任何项目

const Home = (props) => {
    const [deleteItems, setDeleteItems] = useState([]);

    // Subscribe to BackHandler once the component is mounted
    // or when deletedItems changes
    useEffect(() => {
      const handler = BackHandler.addEventListener('hardwareBackPress', () => {
        // If no deleted items: we return false
        if (!deletedItems.length) {
          return false;
        }

        // clear the selected items, and indicate that the back if handled
        setDeletedItems([]);
        return true;
      });

      // unsubscribe when component unmounts
      return () => {
        handler.remove();
      };
    }, [deletedItems]);

    const renderItem = ({ item }) => {
    let bb_OR_ub = item.useBy ? 'Use By ' : 'Best Before '
    let exp_check = CheckExp(item, 1);
    let listStyle = {};
    if (exp_check === -1)
      listStyle = { backgroundColor: '#ff9ea5' }
    else if (exp_check === 0)
      listStyle = { backgroundColor: '#fff185' }
    if (deleteItems.indexOf(item.name) != -1) {
      listStyle = { opacity: 0.3 }
    }
    return (
      <ListItem
        containerStyle={listStyle}
        badge={
          exp_check !== 1 ?
            exp_check === -1 ? { status: 'error', value: `!` } : {
              status: 'warning'
            } : null
        }
        title={item.name}
        subtitle={bb_OR_ub + item.date}
        bottomDivider
        leftAvatar={{ source: require('../shared/cexp.png'), imageProps: { resizeMode: 'contain' } }}
        onPress={() => {
          if (deleteItems.length == 0)
            navigate('ExpiryDetails', { item })
          else {
            setDeleteItems([...deleteItems, item.name])
          }
        }}
        onLongPress={() => {
          if (deleteItems.indexOf(item.name) == -1 || deleteItems.length == 0) {
            setDeleteItems([...deleteItems, item.name])
          }
          else {
            setDeleteItems(deleteItems.filter(el => el != item.name))
          }
        }} />
    );
  }