首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >反应本机:如何实现2列刷卡?

反应本机:如何实现2列刷卡?
EN

Stack Overflow用户
提问于 2022-02-25 15:40:51
回答 1查看 654关注 0票数 4

我正在尝试实现2列中的可滚动卡片列表。卡片应该是可刷的左或右的屏幕外,以被删除。

基本上,这应该像Chrome应用程序当前显示标签列表的方式一样,这些选项卡可以被滑动关闭。参见示例图像这里

我可以使用FlatList实现2列的卡片列表。不过,我在刷卡方面有困难.我尝试了反应-火药卡,但它不能限制刷卡上下,因此列表变得不可滚动。反应-本机甲板-刷卡也不能很好地与列表。

任何帮助都是非常感谢的。谢谢!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-06 20:14:16

我将实现满足以下要求的组件:

  1. 创建一个两列FlatList,其项目是您的卡。
  2. 实现一个手势处理,以识别swipeLeftswipeRight操作,这将删除刷卡。
  3. swipe动作应该是动画的,这意味着我们有某种drag of the screen行为。

我将使用基本的react-带有numColumns={2}反应-本机-滑动-列表-视图的本机numColumns={2}反应-本机-滑动-列表-视图来处理swipeLeftswipeRight动作以及所需的动画。

我将实现一个fire and forget操作,因此在删除一个项之后,它就永远消失了。如果希望能够还原已删除的项,稍后我们将实现restore mechanism

我的初步实现工作如下:

  1. 创建一个带有FlatListnumColumns={2}和一些附加的虚拟样式,以添加一些空白。
  2. 使用useState创建状态,它包含一个表示我们卡的对象数组。
  3. 实现从提供id的状态中移除项的函数。
  4. 将要在SwipeRow中呈现的项包装起来。
  5. removeItem函数传递给swipeGestureEnded支柱。
代码语言:javascript
复制
import React, { useState } from "react"
import { FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"

const data = [
  {
    id: "0",
    title: "Title 1",
  },
  {
    id: "1",
    title: "Title 2",
  },
  {
    id: "2",
    title: "Title 3",
  },
  {
    id: "3",
    title: "Title 4",
  },
  {
    id: "4",
    title: "Title 5",
  },
  {
    id: "5",
    title: "Title 6",
  },
  {
    id: "6",
    title: "Title 7",
  },
  {
    id: "7",
    title: "Title 8",
  },
]

export function Test() {
  const [cards, setCards] = useState(data)
  const [removed, setRemoved] = useState([])

  function removeItem(id) {
    let previous = [...cards]
    let itemToRemove = previous.find((x) => x.id === id)
    setCards(previous.filter((c) => c.id !== id))
    setRemoved([...removed, itemToRemove])
  }

  return (
    <SafeAreaView style={{ margin: 20 }}>
      <FlatList
        data={cards}
        numColumns={2}
        keyExtractor={(item) => item.id}
        renderItem={({ index, item }) => (
          <SwipeRow swipeGestureEnded={() => removeItem(item.id)}>
            <View />
            <View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
              <Text>{item.title}</Text>
            </View>
          </SwipeRow>
        )}
      />
    </SafeAreaView>
  )
}

注意,我们需要对象中的某种属性来确定要删除哪个属性。我在这里使用了一个基本的id属性,这在使用FlatList时很常见。如果您要从不提供相同APIid中检索数据,那么我们可以先做一些预处理(normalization),然后自己添加id支柱。

初始视图如下所示。

滑动,让我们假设‘标题6’的项目在右边或左边移除它。

也可能希望实现以下特性。

  • 如果项目位于第一列中,则只有向左滑动才会删除该项。
  • 如果项目在第二列中,则只有向右滑动才会删除该项。

这很容易使用传递给renderItem函数的index参数和传递给swipeGestureEnded函数的gestureStatevx支柱来实现。

这是充分发挥作用的实施。

代码语言:javascript
复制
import React, { useState } from "react"
import { FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"

const data = [
  {
    id: "0",
    title: "Title 1",
  },
  {
    id: "1",
    title: "Title 2",
  },
  {
    id: "2",
    title: "Title 3",
  },
  {
    id: "3",
    title: "Title 4",
  },
  {
    id: "4",
    title: "Title 5",
  },
  {
    id: "5",
    title: "Title 6",
  },
  {
    id: "6",
    title: "Title 7",
  },
  {
    id: "7",
    title: "Title 8",
  },
]

export function Test() {
  const [cards, setCards] = useState(data)
  const [removed, setRemoved] = useState([])

  function removeItem(id) {
    let previous = [...cards]
    let itemToRemove = previous.find((x) => x.id === id)
    setCards(previous.filter((c) => c.id !== id))
    setRemoved([...removed, itemToRemove])
  }

  return (
    <SafeAreaView style={{ margin: 20 }}>
      <FlatList
        data={cards}
        numColumns={2}
        keyExtractor={(item) => item.id}
        renderItem={({ index, item }) => (
          <SwipeRow
            swipeGestureEnded={(key, event) => {
              if (event.gestureState.vx < 0) {
                if (index % 2 === 0) {
                  removeItem(item.id)
                }
              } else if (event.gestureState.vx >= 0) {
                if (index % 2 === 1) {
                  removeItem(item.id)
                }
              }
            }}
            disableLeftSwipe={index % 2 === 1}
            disableRightSwipe={index % 2 === 0}>
            <View />
            <View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
              <Text>{item.title}</Text>
            </View>
          </SwipeRow>
        )}
      />
    </SafeAreaView>
  )
}

由于索引在FlatList中是零的,所以一个项在第二列中当且仅当index % 2 === 1 (例如,具有索引3的项总是在第二列中,因此不能被2整除),另一方面,一个项在第一列中当且仅当为indexindex % 2 === 0可被2整除。

SwipeRowComponent中有几个回调函数道具,在某些情况下应该触发。然而,他们中的大多数没有在我的设置中工作,我仍然不知道为什么。我让它通过使用event.gestureState.vx属性来工作,如果我们向左滑动,它是负的,如果我们向右滑动,它是正的(包括零)。

我们可能希望实现一个undo按钮,因为它在这类功能中非常常见。这样做的办法如下:

  • 实现第二种状态,它表示保存最后删除项的Queue。然后,undo按钮只pops最后删除的项。

下面是一个完全工作的实现,它使用一个虚拟的undo按钮来实现这一点。

代码语言:javascript
复制
import React, { useState } from "react"
import { Button, FlatList, SafeAreaView, Text, View } from "react-native"
import { SwipeRow } from "react-native-swipe-list-view"

const data = [
  {
    id: "0",
    title: "Title 1",
  },
  {
    id: "1",
    title: "Title 2",
  },
  {
    id: "2",
    title: "Title 3",
  },
  {
    id: "3",
    title: "Title 4",
  },
  {
    id: "4",
    title: "Title 5",
  },
  {
    id: "5",
    title: "Title 6",
  },
  {
    id: "6",
    title: "Title 7",
  },
  {
    id: "7",
    title: "Title 8",
  },
]

export function Test() {
  const [cards, setCards] = useState(data)
  const [removed, setRemoved] = useState([])

  function removeItem(id) {
    let previous = [...cards]
    let itemToRemove = previous.find((x) => x.id === id)
    setCards(previous.filter((c) => c.id !== id))
    setRemoved([...removed, itemToRemove])
  }

  function undoRemove() {
    if (removed && removed.length > 0) {
      let itemToUndo = removed[removed.length - 1]
      setCards([...cards, itemToUndo])
      setRemoved(removed.filter((c) => c.id !== itemToUndo.id))
    }
  }

  return (
    <SafeAreaView style={{ margin: 20 }}>
      <FlatList
        data={cards}
        numColumns={2}
        keyExtractor={(item) => item.id}
        renderItem={({ index, item }) => (
          <SwipeRow
            swipeGestureEnded={(key, event) => {
              if (event.gestureState.vx < 0) {
                if (index % 2 === 0) {
                  removeItem(item.id)
                }
              } else if (event.gestureState.vx >= 0) {
                if (index % 2 === 1) {
                  removeItem(item.id)
                }
              }
            }}
            disableLeftSwipe={index % 2 === 1}
            disableRightSwipe={index % 2 === 0}>
            <View />
            <View style={{ margin: 20, borderWidth: 1, padding: 20 }}>
              <Text>{item.title}</Text>
            </View>
          </SwipeRow>
        )}
      />
      <Button onPress={undoRemove} title="Undo" />
    </SafeAreaView>
  )
}

注意,我的undo按钮只是将删除的项附加到列表的末尾。如果要保留初始索引,则需要保存旧索引并将项目推送到正确位置。

下面是我最后一个实现的工作中的小吃

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71268248

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档