web-dev-qa-db-fra.com

Grille ListView dans React Native

Je construis une application simple dans React Native qui récupère les listes à partir d'une source JSON distante et les affiche à l'écran. 

Jusqu'ici, en utilisant l'excellent exemple ici, j'ai réussi à faire afficher les résultats en utilisant les composants ListView en lignes (c'est-à-dire 1 résultat par ligne, voir la capture d'écran). J'ai besoin que les résultats s'affichent dans une grille, c'est-à-dire 3 à 6 éléments par ligne, en fonction de la taille et de l'orientation de l'écran.

Quel est le meilleur moyen d’obtenir ces résultats dans une grille? Puis-je utiliser ListView pour cela, ou est-ce seulement pour des résultats d'un rang par ligne? J'ai essayé de jouer avec les styles flexbox, mais comme React ne semble pas accepter les valeurs% et que ListView n'accepte pas les styles, je n'ai pas encore eu de succès.

This is how my listings are displaying at the moment - one per row.

59
Mateo

Vous devez utiliser une combinaison de flexbox et la connaissance que ListView englobe ScrollView et prend ainsi ses propriétés. Dans cet esprit, vous pouvez utiliser la propriété contentContainerStyle du ScrollView pour styliser les éléments.

var TestCmp = React.createClass({
    getInitialState: function() {
      var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
      var data = Array.apply(null, {length: 20}).map(Number.call, Number);
      return {
        dataSource: ds.cloneWithRows(data),
      };
    },

    render: function() {
      return (
        <ListView contentContainerStyle={styles.list}
          dataSource={this.state.dataSource}
          renderRow={(rowData) => <Text style={styles.item}>{rowData}</Text>}
        />
      );
    }
});

Juste une liste avec des données factices. Notez l'utilisation de contentContainerStyle. Voici l'objet de style:

var styles = StyleSheet.create({
    list: {
        flexDirection: 'row',
        flexWrap: 'wrap'
    },
    item: {
        backgroundColor: 'red',
        margin: 3,
        width: 100
    }
});

Nous disons au conteneur que nous voulons des éléments dans une ligne d'habillage et définissons la largeur de chaque objet enfant.

Screenshot

121
Colin Ramsay

J'ajoute ceci comme réponse car les commentaires de débordement sont cachés sous la réponse de Colin Ramsay: à partir de React Native 0.28, vous avez également besoin de alignItems: 'flex-start', dans le style de la liste ou le flexWrap ne fonctionnera pas. Merci à Kerumen sur codedump.io pour cela. Alors,

var styles = StyleSheet.create({
list: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    alignItems: 'flex-start',
},

... avec le reste comme dans la réponse de Colin.

35
kwishnu

ListView est maintenant obsolète et vous devez utiliser FlatList à la place. FlatList a un accessoire appelé numColumns qui correspond exactement à ce que nous voulons créer une grille défilable.

Par exemple:

const data = [
  {id: 'a', value: 'A'},
  {id: 'b', value: 'B'},
  {id: 'c', value: 'C'},
  {id: 'd', value: 'D'},
  {id: 'e', value: 'E'},
  {id: 'f', value: 'F'},
];
const numColumns = 3;
const size = Dimensions.get('window').width/numColumns;
const styles = StyleSheet.create({
  itemContainer: {
    width: size,
    height: size,
  },
  item: {
    flex: 1,
    margin: 3,
    backgroundColor: 'lightblue',
  }
});

function Grid(props) {
  return (
    <FlatList
      data={data}
      renderItem={({item}) => (
        <View style={styles.itemContainer}>
          <Text style={styles.item}>{item.value}</Text>
        </View>
      )}
      keyExtractor={item => item.id}
      numColumns={numColumns} />
  );
}

 enter image description here

Ce blog billet explique très bien les nouvelles fonctionnalités de FlatList.

Remarque: pour une raison quelconque, vous devez utiliser keyExtractor sur la FlatList au lieu du prop key typique de chaque élément. Sinon, vous recevrez un avertissement. Le code de base FlatList renvoie Warning - React Native

25
Tim Perkins

J'ai eu le même problème et j'ai écrit un composant qui résout ce problème, vous pouvez le trouver ici: https://github.com/pavlelekic/react-native-gridview

En outre, ce composant permet également de s'assurer que la largeur des éléments est un nombre entier, de sorte que les bordures des éléments ne comportent pas d'antialiasing, elles sont claires et nettes.

7
Pavle Lekic

Suivez la réponse de Colin Ramsay. Et si vous voulez une demi-largeur pour chaque article, essayez de cette façon.

...
import { Dimensions } from 'react-native'; 
const { width, height } = Dimensions.get('window');
const Gutter = 0; // You can add Gutter if you want
...


const styles = StyleSheet.create({
  item: {
    width: (width - Gutter * 3)/2,
    marginBottom: Gutter,
    flexDirection: 'column',
    alignSelf: 'flex-start',
    backgroundColor: '#ff0000',
  },
  list: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    flexWrap: 'wrap',
    paddingHorizontal: Gutter,
  },
});
5
Yi Feng Xie

utilisez FlatList au lieu de Listview dans react-native. il a plus de valeur ajoutée et de meilleures performances. consultez l'exemple ici

2
Moorthy

Vous pouvez utiliser FlatList et définir numColumns pour obtenir le résultat identique à celui de grid

1
nguyencse

Consultez l'exemple suivant en utilisant le composant FlatList de React Native pour prendre en charge le défilement infini avec d'excellentes performances:

//Resize the grid
onLayout = (event) => { 
  const {width} = event.nativeEvent.layout;
  const itemWidth = 150
  const numColumns = Math.floor(width/itemWidth)
  this.setState({ numColumns: numColumns })
}

render() {
  return (
    <Content 
     contentContainerStyle={{flex:1, alignItems: 'center'}}
     onLayout={this.onLayout}>
       <FlatList
        data={this.state.products}
        keyExtractor={(item, index) => index}
        key={this.state.numColumns}
        numColumns={this.state.numColumns}
        renderItem={({item}) => 
          <ListThumb //Custom component, it's only an example.
           navigation={navigation}
           brand={item.brand}
           price={item.price}
           imageSource={item.image_link}/>
        }
     />
   </Content>
  )
}    

L'exemple ressemble à

 enter image description here

Cordialement, Nicholls

0
jdnichollsc