React Native:创建一个可拖动和可滑动的列表

如今,能够在移动应用程序中滑动列表项的任何人都不会感到惊讶。 我们的本机应用程序之一也具有这种功能,但是最近需要通过拖放列表项的功能对其进行扩展。 由于寻找解决方案的过程使我花费了一定数量的神经细胞,因此我决定撰写一篇简短的文章,以节省后代的宝贵时间。



在我们的应用程序中,我们使用了react-native-swipe-list-view包来创建可滑动列表。 首先想到的是带一些具有拖放功能的包装,然后用蛇穿过刺猬。

互联网搜索给出了三个候选者: react-native-draggable-listreact-native-sortable-listreact-native-draggable-flatlist

使用第一个程序包,甚至无法运行所附的示例(但是,不仅对我而言,在Issues中指出了相应的问题 )。

我不得不修改第二个程序包,但是我设法创建了一个可拖动和可滑动的列表。 但是,结果并没有得到启发-组件无耻地闪烁:重绘闪烁,元素超出列表范围甚至消失。 很明显,不能以这种形式使用它。

起初,最后一个包装也表现得像个反复无常的女士,但后来证明我只是不知道如何烹饪。 找到了这个“女士”核心的钥匙后,我设法取得了令人满意的结果。

在我们的项目中,有一个可滑动列表,您需要将其固定在其上,但实际上,最好从另一边开始:首先创建一个可拖动列表,然后添加滑动功能。

读者应该知道如何创建本机项目,因此让我们集中精力创建所需的列表。 下面讨论的示例是TypeScript代码。

制作可拖动列表


因此,让我们从安装软件包开始:

 yarn add react-native-draggable-flatlist 

我们导入必要的模块:

 import React, { Component } from 'react' import { View } from 'react-native' import styles from './styles' import DraggableFlatList, { RenderItemInfo, OnMoveEndInfo } from 'react-native-draggable-flatlist' import ListItem from './components/ListItem' import fakeData from './fakeData.json' 

这里DraggableFlatList是安装的软件包中的一个组件,实现了拖放ListItemListItem是我们用于显示列表项的组件(代码将在下面显示), fakeData是一个包含假数据的json文件-在这种情况下,是以下形式的对象数组:

 {"id": 0, "name": "JavaScript", "favorite": false} 

在实际的应用程序中,这些数据很可能会从道具中传到您的组件中,或者从网络上下载,但是在我们的情况下,我们的管理工作很少。

由于在此示例中使用了TypeScript,因此我们描述了一些实体:

 type Language = { id: number, name: string, favorite: boolean, } interface AppProps {} interface AppState { data: Array<Language> } 

Language类型告诉我们列表项将包含哪些字段。

在此示例中,我们不会从道具中得到任何东西,因此AppProps接口是微不足道的,在故事中,我们将存储一个Language对象数组,该数组在AppState接口中指示。

由于组件代码不是很大,因此我将完整介绍它:

应用组件代码
 class App extends Component<AppProps, AppState> { constructor(props: AppProps) { super(props) this.state = { data: fakeData, } } onMoveEnd = ({ data }: OnMoveEndInfo<Language>) => { this.setState({ data: data ? [...data] : [] }) } render() { return ( <View style={styles.root}> <DraggableFlatList data={this.state.data} renderItem={this.renderItem} keyExtractor={(item) => item.id.toString()} scrollPercent={5} onMoveEnd={this.onMoveEnd} /> </View> ) } renderItem = ({ item, move, moveEnd, isActive }: RenderItemInfo<Language>) => { return ( <ListItem name={item.name} move={move} moveEnd={moveEnd} isActive={isActive} /> ) } } 


移动项目时将调用onMoveEnd方法。 在这种情况下,我们需要将具有新元素顺序的列表置于状态中,因此我们调用this.setState方法。

renderItem方法renderItem一个列表项,并接受RenderItemInfo <Language>类型的对象。 该对象包括以下字段:

  • item-作为数据传递到列表的数组的下一个元素,
  • movemoveEnd是在移动列表项时调用的函数, DraggableFlatList组件提供了这些函数,
  • isActive是一个布尔型字段,用于确定该项目当前是否可拖动。

实际上,用于显示列表项的组件是moveEnd ,当长时间按下该组件时,它会调用move ,而在释放时,它会moveEnd

ListItem组件代码
 import React from 'react' import { Text, TouchableOpacity } from 'react-native' import styles from './styles' interface ListItemProps { name: string, move: () => void, moveEnd: () => void, isActive: boolean, } const ListItem = ({ name, move, moveEnd, isActive }: ListItemProps) => { return ( <TouchableOpacity style={[styles.root, isActive && styles.active]} onLongPress={move} onPressOut={moveEnd} > <Text style={styles.text}>{name}</Text> </TouchableOpacity> ) } export default ListItem 


所有组件的样式都移到单独的文件中,在此处未给出,但是可以在资源库中查看它们。

结果为:



新增滑动功能


好吧,我们成功地应对了第一部分,然后继续进行Marlezon芭蕾舞的第二部分。

为了增加滑动列表项的功能,我们使用react-native-swipe-list-view包。

首先,让我们安装它:

 yarn add react-native-swipe-list-view 

该软件包包含SwipeRow组件,根据文档,该组件应包括两个组件:

 <SwipeRow> <View style={hiddenRowStyle} /> <View style={visibleRowStyle} /> </SwipeRow> 

请注意,第一个视图绘制在第二个视图下方。

让我们更改ListItem组件的代码。

ListItem组件代码
 import React from 'react' import { Text, TouchableOpacity, View, Image } from 'react-native' import { SwipeRow } from 'react-native-swipe-list-view' import { Language } from '../../App' import styles from './styles' const heart = require('./icons8-heart-24.png') const filledHeart = require('./icons8-heart-24-filled.png') interface ListItemProps { item: Language, move: () => void, moveEnd: () => void, isActive: boolean, onHeartPress: () => void, } const ListItem = ({ item, move, moveEnd, isActive, onHeartPress }: ListItemProps) => { return ( <SwipeRow rightOpenValue={-180}> <View style={styles.hidden}> <TouchableOpacity onPress={onHeartPress}> <Image source={item.favorite ? filledHeart : heart} /> </TouchableOpacity> </View> <TouchableOpacity activeOpacity={1} style={[styles.root, isActive && styles.active]} onLongPress={move} onPressOut={moveEnd} > <Text style={styles.text}>{item.name}</Text> </TouchableOpacity> </SwipeRow> ) } export default ListItem 


首先,我们添加了带有SwipeRow属性的SwipeRow组件,该组件确定了可以滑动元素的距离。

其次,我们在SwipeRow移动了SwipeRow并添加了一个View,它将在此按钮下绘制。

在此视图中,将绘制一张图片以确定该语言是否是您的最爱。 单击它时,该值应取反,并且由于数据在父组件中,因此您需要在此处引发执行此操作的回调。

对父组件进行必要的更改:

应用组件代码
 import React, { Component } from 'react' import { View } from 'react-native' import styles from './styles' import DraggableFlatList, { RenderItemInfo, OnMoveEndInfo } from 'react-native-draggable-flatlist' import ListItem from './components/ListItem' import fakeData from './fakeData.json' export type Language = { id: number, name: string, favorite: boolean, } interface AppProps {} interface AppState { data: Array<Language> } class App extends Component<AppProps, AppState> { constructor(props: AppProps) { super(props) this.state = { data: fakeData, } } onMoveEnd = ({ data }: OnMoveEndInfo<Language>) => { this.setState({ data: data ? [...data] : [] }) } toggleFavorite = (value: Language) => { const data = this.state.data.map(item => ( item.id !== value.id ? item : { ...item, favorite: !item.favorite } )) this.setState({ data }) } render() { return ( <View style={styles.root}> <DraggableFlatList data={this.state.data} renderItem={this.renderItem} keyExtractor={(item) => item.id.toString()} scrollPercent={5} onMoveEnd={this.onMoveEnd} /> </View> ) } renderItem = ({ item, move, moveEnd, isActive }: RenderItemInfo<Language>) => { return ( <ListItem item={item} move={move} moveEnd={moveEnd} isActive={isActive} onHeartPress={() => this.toggleFavorite(item)} /> ) } } export default App 


该项目的源代码在GitHub上

结果显示如下:

Source: https://habr.com/ru/post/zh-CN460567/


All Articles