React-Native Listview组件用法详解
ListView作为React Native的核心组件,用于高效地显示一个可以垂直滚动的变化的数据列表。其中最重要的属性之一是DataSource,列表依赖的数据源,用于实例化一个ListView对象。此外ListView可以使用所有ScrollView的属性。一个最简单的例子:
constructor(props) { super(props); var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.state = { dataSource: ds.cloneWithRows(['row 1', 'row 2']), }; } render() { return ( <ListView dataSource={this.state.dataSource} renderRow={(rowData) => <Text>{rowData}</Text>} /> ); }
然而实际开发中往往遇到更复杂的需求,本文将介绍如何使用ListView一些高级特性解决难题。
需求描述:实现一个分组商品列表,最终陈列效果为多行多列。具体如图标红区域。
需求拆分:ListView Section,每一块(Section)包含标题+内容。渲染块标题的函数为renderSectionHeader;渲染条目数据的函数为renderRow。难点在于每一行显示两条数据,ListView contentContainerStyle属性赋值为如下对象,出现只能显示一行的bug,不分组的ListView不会出现这个问题。
{ flexDirection: 'row', flexWrap: 'wrap' }
最终解决方案:在ListView 和单条目数据Row之间,插入一个组件,该组件数据源为行数据(长度为2或者1的数组),以此实现多行多列。也就是说renderRow函数从渲染单条目数据,转为渲染一行所需的数据。
import React ,{ Component } from 'react'; import { View, ListView, Text, Image, TouchableHighlight, StyleSheet, Dimensions, } from 'react-native'; const windowWidth = Dimensions.get('window').width; var { ListJSON } = require('../JSON/ListJSON'),Utils = require('./Utils'); class ProductInfo extends Component{ constructor(props){ super(props); } _gotoBuy(){ } render(){ var product = this.props.product; return ( <TouchableHighlight onPress={this._gotoBuy} underlayColor={'rgba(0, 0, 0, 0.3)'} style={styles.productWrapper}> <View style={styles.productDetail}> <Text style={styles.productDetailTxt}>{product.flowTotalUnit}</Text> <Text style={styles.productDetailTxt}>/{product.retailPrice / 100}元</Text> <Text style={styles.productDetailTxt}>{!!product.expiredPrice ? (product.expiredPrice / 100) + '元' : ''}</Text> </View> </TouchableHighlight> ); } } class ProductRow extends Component{ constructor(props){ super(props); } render(){ return ( <View style={styles.productFlex}> { this.props.products.map((item,i) => <ProductInfo product={ item } key = { i }></ProductInfo>) } </View> ) } } class List extends Component{ constructor(props){ super(props); var _getSectionData = (dataBlob, sectionID) => { return dataBlob[sectionID]; } var _getRowData = (dataBlob, sectionID, rowID) => { return dataBlob[sectionID + ':' + rowID]; } var data = Utils.translateData(ListJSON); const ds = new ListView.DataSource({ getSectionData: _getSectionData, getRowData: _getRowData, rowHasChanged: (row1, row2) => row1 !== row2, sectionHeaderHasChanged: (s1, s2) => s1 !== s2 }); this.state = { dataSource: ds.cloneWithRowsAndSections(data.dataBlob, data.sectionIDs, data.rowIDs), } } renderRow(rowData, sectionID, rowID) { //console.log(rowData,'****') return ( <ProductRow products={rowData}></ProductRow> ); } renderSectionHeader(sectionData, sectionID){ return ( <View> <Text style={styles.sectionTtl}>{sectionData.scope}{sectionData.type}<Text> | {sectionData.tip}</Text></Text> </View> ); } render(){ return ( <View style={styles.container} > <ListView dataSource={this.state.dataSource} contentContainerStyle={styles.listview} renderRow = {this.renderRow} renderSectionHeader = {this.renderSectionHeader} /> </View> ); } } const styles = StyleSheet.create({ container:{ padding:10, }, listview: { width:windowWidth-20 }, sectionTtl:{ height:30, textAlignVertical:'center' }, productFlex:{ paddingBottom:10, flexDirection:'row', justifyContent:'space-between' }, productWrapper:{ width:(windowWidth-20)*0.485, borderWidth:1, borderColor:'#e2e2e2', borderRadius:4 }, productDetail:{ flexDirection: 'row', justifyContent:'center' }, productDetailTxt:{ height:56, textAlignVertical:'center', } }); module.exports = { List:List }
分组列表需要的数据格式如下:
{ dataBlob: dataBlob, sectionIDs: sectionIDs, rowIDs: rowIDs }
处理section数据方法如下
dataBlob['s'+sectionIdx] = { "scope": scopev.scopeName, "type": typeV.typeName, "tip": tip } sectionIDs.push('s'+sectionIdx); rowIDs[sectionIdx] = []; var rowIdx = 0; _.forEach(list, function (item,index) { let rowAttr = 's'+sectionIdx+':'+rowIdx; if(index%2==0){ dataBlob[rowAttr] = []; rowIDs[sectionIdx].push(rowIdx); }else{ rowIdx++; } dataBlob[rowAttr].push(item); }); sectionIdx++;