ListView做爲React Native的核心組件,用於高效地顯示一個能夠垂直滾動的變化的數據列表。其中最重要的屬性之一是DataSource,列表依賴的數據源,用於實例化一個ListView對象。此外ListView能夠使用全部ScrollView的屬性。一個最簡單的例子:javascript
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一些高級特性解決難題。java
需求描述:實現一個分組商品列表,最終陳列效果爲多行多列。具體如圖標紅區域。react
需求拆分:ListView Section,每一塊(Section)包含標題+內容。渲染塊標題的函數爲renderSectionHeader;渲染條目數據的函數爲renderRow。難點在於每一行顯示兩條數據,ListView contentContainerStyle屬性賦值爲以下對象,出現只能顯示一行的bug,不分組的ListView不會出現這個問題。react-native
{ 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 }
分組列表須要的數據格式以下:app
{ 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++;