React Native: créez une liste glissable et glissable

Aujourd'hui, il est difficile de surprendre quiconque avec la possibilité de balayer les éléments de la liste dans les applications mobiles. L'une de nos applications natives réactives avait également une telle fonctionnalité, mais récemment, il était nécessaire de l'étendre avec la possibilité de glisser-déposer des éléments de liste. Et comme le processus de recherche d'une solution m'a coûté une certaine quantité de cellules nerveuses, j'ai décidé de déposer un court article afin de gagner un temps précieux pour les générations futures.



Dans notre application, nous avons utilisé le package react react-native-swipe-list-view pour créer la liste swipeable. La première pensée a été de prendre un paquet avec la fonctionnalité drag'n'drop et de traverser le hérisson avec un serpent.

Une recherche sur Internet a donné trois candidats: react-native-draggable-list react-native-draggable-flatlist react-native-sortable-list react-native-draggable-flatlist .

En utilisant le premier package, il n'a pas été possible d'exécuter même l'exemple ci-joint (cependant, non seulement pour moi, le problème correspondant est indiqué dans les problèmes ).

J'ai dû bricoler avec le deuxième paquet, mais j'ai réussi à créer une liste glissable et glissable. Cependant, le résultat n'a pas été inspiré - le composant clignotait sans vergogne: clignotement du redessin, chute d'éléments bien au-delà de la liste, ou même leur disparition. Il est devenu clair que sous cette forme, il ne peut pas être utilisé.

Au début, le dernier paquet s'est également comporté comme une dame capricieuse, mais il s'est avéré que je ne savais tout simplement pas comment le cuisiner. Ayant ramassé une clé au cœur de cette "dame", j'ai réussi à obtenir un résultat acceptable.

Dans notre projet, il y avait une liste glissable à laquelle vous devez fixer le glisser-déposer, mais en pratique, il est préférable de partir de l'autre bord: faites d'abord une liste glissable, puis ajoutez la possibilité de glisser.

Les lecteurs sont censés savoir comment créer un projet natif réactif, alors concentrons-nous sur la création de la liste dont nous avons besoin. L'exemple présenté ci-dessous est du code TypeScript.

Faire une liste glissable


Commençons donc par installer le package:

 yarn add react-native-draggable-flatlist 

Nous importons les modules nécessaires:

 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' 

Ici, DraggableFlatList est un composant du package installé qui implémente la fonction glisser-déposer, ListItem est notre composant pour afficher un élément de liste (le code sera présenté ci-dessous), fakeData est un fichier json qui contient de fausses données - dans ce cas, un tableau d'objets de la forme:

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

Dans une application réelle, ces données proviendront très probablement de votre composant à partir d'accessoires ou seront téléchargées à partir du réseau, mais dans notre cas, nous gérerons avec peu de sang.

Puisque TypeScript est utilisé dans cet exemple, nous décrivons certaines entités:

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

Le type de Language nous indique quels champs les éléments de liste auront.

Dans cet exemple, nous n'obtiendrons rien des accessoires, donc l'interface AppProps est triviale, et dans l'histoire, nous allons stocker un tableau d'objets Language , qui est indiqué dans l'interface AppState .

Étant donné que le code du composant n'est pas très volumineux, je vais le donner dans son intégralité:

Code de composant d'application
 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} /> ) } } 


La méthode onMoveEnd appelée lorsque l'élément est déplacé. Dans ce cas, nous devons mettre la liste avec le nouvel ordre des éléments dans l'état, nous appelons donc la méthode this.setState .

La méthode renderItem un élément de liste et accepte un objet de type RenderItemInfo <Language>. Cet objet comprend les champs suivants:

  • item - l'élément suivant du tableau passé en tant que données à la liste,
  • move et moveEnd sont des fonctions qui sont appelées lorsqu'un élément de liste est déplacé, le composant DraggableFlatList fournit ces fonctions,
  • isActive est un champ booléen qui détermine si l'élément est actuellement déplaçable.

Le composant pour afficher l'élément de liste est, en fait, TouchableOpacity , qui, lorsqu'il est pressé pendant longtemps, appelle move , et lorsqu'il est relâché, il se moveEnd .

Code composant 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 


Les styles de tous les composants sont déplacés vers des fichiers séparés et ne sont pas indiqués ici, mais ils peuvent être affichés dans le référentiel .

Le résultat résultant:



Ajoutez la possibilité de glisser


Eh bien, nous avons réussi à faire face à la première partie, nous passons à la deuxième partie du ballet Marlezon.

Pour ajouter la possibilité de faire glisser les éléments de la liste, nous utilisons le package react-native-swipe-list-view .

Pour commencer, installons-le:

 yarn add react-native-swipe-list-view 

Ce package contient le composant SwipeRow qui, selon la documentation, devrait inclure deux composants:

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

Notez que la première vue est dessinée sous la seconde.

Modifions le code du composant ListItem .

Code composant 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 


Tout d'abord, nous avons ajouté le composant SwipeRow avec la propriété SwipeRow , qui détermine la distance sur laquelle l'élément peut être glissé.

Deuxièmement, nous avons déplacé notre TouchableOpacity intérieur de SwipeRow et ajouté une vue, qui sera dessinée sous ce bouton.

Dans cette vue, une image est dessinée pour déterminer si la langue est préférée. Lorsque vous cliquez dessus, la valeur doit être inversée et, puisque les données se trouvent dans le composant parent, vous devez lancer ici un rappel qui exécute cette action.

Apportez les modifications nécessaires au composant parent:

Code de composant d'application
 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 


Sources du projet sur GitHub .

Le résultat est présenté ci-dessous:

Source: https://habr.com/ru/post/fr460567/


All Articles