ListView
ListView - 一个核心组件,用于高效地显示一个可以垂直滚动的变化的数据列表。最基本的使用方式就是创建一个ListView.DataSource
数据源,然后给它传递一个普通的数据数组,再使用数据源来实例化一个ListView
组件,并且定义它的renderRow
回调函数,这个函数会接受数组中的每个数据作为参数,返回一个可渲染的组件(作为listview的每一行)。
ListView可以使用所有ScrollView的属性。
属性:contentContainerStyleStyleSheetPropType(ViewStylePropTypes) //设置列表容器的属性
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 contentContainerStyle={styles.contentContainer} //设置列表容器的属性 dataSource={this.state.dataSource} renderRow={(rowData) => <Text>{rowData}</Text>} /> ); }
更多属性见官网.....
ListView.DataSource
ListViewDataSource
为ListView
组件提供高性能的数据处理和访问。我们需要调用方法从原始输入数据中抽取数据来创建ListViewDataSource
对象,并用其进行数据变更的比较。原始输入数据可以是简单的字符串数组,也可以是复杂嵌套的对象——分不同区(section)各自包含若干行(row)数据。
要更新datasource中的数据,请(每次都重新)调用cloneWithRows
方法(如果用到了section,则对应cloneWithRowsAndSections
方法)。数据源中的数据本身是不可修改的,所以请勿直接尝试修改。clone方法会自动提取新数据并进行逐行对比(使用rowHasChanged方法中的策略),这样ListView就知道哪些行需要重新渲染了。
在下面这个例子中,一个组件在分块接受数据,这些数据由_onDataArrived
方法处理——将新数据拼接(concat)到旧数据尾部,同时使用clone方法更新DataSource。我们使用concat方法来修改this._data
以创建新数组,注意不能使用push方法拼接数组。实现_rowHasChanged
方法需要透彻了解行数据的结构,以便提供高效的比对策略。
constructor(props) { super(props); var ds = new ListView.DataSource({rowHasChanged: (r1,r2) => r1 !== r2}); this.state = { ds,}; this._data = []; } _onDataArrived = (newData) => { this._data = this._data.concat(newData); this.setState({ ds: this.state.ds.cloneWithRows(this._data) }); };
方法
constructor(params)
你可以在构造函数中针对section标题和行数据提供自定义的提取方法和hasChanged
比对方法。如果不提供,则会使用默认的defaultGetRowData
和defaultGetSectionHeaderData
方法来提取行数据和section标题。
{ sectionID_1: { rowID_1: rowData1,... },... }
或者:
{ sectionID_1: [ rowData1,rowData2,... ],... }
或者:
[ [ rowData1,... ]
构造函数可以接受下列四种参数(都是可选):
- getRowData(dataBlob,sectionID,rowID);
- getSectionHeaderData(dataBlob,sectionID);
- rowHasChanged(prevRowData,nextRowData);
- sectionHeaderHasChanged(prevSectionData,nextSectionData);
cloneWithRows(dataBlob,rowIdentities)
根据指定的dataBlob
和rowIdentities
为ListViewDataSource
复制填充数据。dataBlob
即原始数据。需要在初始化时定义抽取函数(否则使用默认的抽取函数)。
rowIdentities
是一个二维数组,包含了行数据对应的id标识符,例如[['a1','a2'],['b1','b2','b3'],...]。如果没有指定此数组,则默认取行数据的key。
注:此方法实际并没有复制数据。它只是重新创建一个datasource,然后将你指定的dataBlob传递给构造函数中指定的提取函数,因而会抛弃先前的数据。如果你希望保留先前的数据,则必须先自行进行新老数据的合并处理,然后再将合并后的结果作为dataBlob
传递给此方法调用。
cloneWithRowsAndSections(dataBlob,sectionIdentities,rowIdentities)
此方法作用基本等同cloneWithRows
,区别在于可以额外指定sectionIdentities
。如果你不需要section,则直接使用cloneWithRows
即可。
sectionIdentities
同理是包含了section标识符的数组。例如['s1','s2',...]。如果没有指定此数组,则默认取section的key。
注:此方法会返回新的对象!
getRowCount()
getRowAndSectionCount()
rowShouldUpdate(sectionIndex,rowIndex)
返回值表明某行数据是否已变更,需要重新渲染。
getRowData(sectionIndex,rowIndex)
返回渲染行所需的数据(指定如何从原始dataBlob中提取数据)。
getRowIDForFlatIndex(index)
给定索引值,求其对应rowID。如果查找不到则返回null。
getSectionIDForFlatIndex(index)
给定索引值,求其对应sectionID。如果查找不到则返回null。
getSectionLengths()
返回一个数组,包含每个section的行数量。
sectionHeaderShouldUpdate(sectionIndex)
返回值用于说明section标题是否需要重新渲染。
getSectionHeaderData(sectionIndex)
仿IOS分组吸顶效果:
//模拟IOS的通讯录 import React,{ Component } from 'react'; import { AppRegistry,StyleSheet,Text,View,Image,ListView,TouchableOpacity } from 'react-native'; //模拟数据 var ListData={ data:[ //组一 { cars:[ { "icon":"QQ","name":"奥迪" },{ "icon":"QQ","name":"阿尔法" },"name":"阿斯顿马丁" },"name":"ALPIMA" },"name":"安凯客车" },"name":"ARCFOX" } ],title:"A" },// 组二 { cars:[ { "icon":"QQ","name":"保时捷" },"name":"本田" },"name":"别克" },"name":"奔驰" },"name":"宝马" },"name":"宝骏" },"name":"比亚迪" },"name":"标志" },"name":"北京" },"name":"北汽幻速" },"name":"宝沃" },"name":"奔腾" },"name":"北汽绅宝" },"name":"宾利" },"name":"比速汽车" },"name":"北汽制造" },"name":"北汽威旺" },"name":"北汽新能源" },"name":"巴博斯" },"name":"布加迪" },"name":"北汽到达" } ],title:"B" },// 组三 { cars:[ { "icon":"QQ","name":"长安" },"name":"长安酷尚" },"name":"长城" },"name":"长安轻型车" },"name":"昌河" },"name":"成功汽车" },"name":"长安跨越" } ],title:"C" } ] } var ListViewDemo3=React.createClass({ getInitialState(){ //获取组中的数据 var getSectionData=(dataBlob,sectionID) => { return dataBlob[sectionID]; } //获取行中的数据 var getRowData=(dataBlob,rowID)=>{ return dataBlob[sectionID+":"+rowID] }; return{ dataSource:new ListView.DataSource({ getSectionData:getSectionData,//获取组中的数据 getRowData:getRowData,//获取行中的数据 rowHasChanged:(r1,r2) => r1 !== r2,sectionHeaderHasChanged:(s1,s2) => s1 !== s2,}) } },//每一行中的数据 renderRow(rowData){ return( <TouchableOpacity activeOpacity={0.5}> <View style={styles.rowStyle}> <Image style={styles.rowImg} source={{uri:rowData.icon}}/> <Text>{rowData.name}</Text> </View> </TouchableOpacity> ) },//每一组中的数据 renderSectionHeader(sectionData,sectionID){ return( <View style={styles.sectionHeader}> <Text style={{marginLeft:10,marginTop:5,color:"red"}}>{sectionData}</Text> </View> ) },render(){ return ( <View style={styles.outerView}> {/*头部*/} <View style={styles.headerView}> <Text style={{color:"#fff",fontSize:25}}>see MyGo品牌</Text> </View> <ListView dataSource={this.state.dataSource} renderRow={this.renderRow} renderSectionHeader={this.renderSectionHeader} /> </View> ) },//复杂的操作:数据请求或者异步操作等等 componentDidMount(){ //调用JSON数据 this.loadDataFromJson(); },loadDataFromJson(){ //拿到JSON数据 var jsonData=ListData.data; //定义一些变量 var dataBlob={},sectionIDs=[],rowIDs=[],cars=[]; //遍历 for(var i=0;i<jsonData.length;i++){ //1、把组号放入sectionIDs数组中 sectionIDs.push(i); //2、把组中的内容放入dataBlob对象中 dataBlob[i]=jsonData[i].title; //3、取出该组中所有的车 cars = jsonData[i].cars; rowIDs[i]=[]; //4、遍历所有的车数组 for(var g=0;g<cars.length;g++){ //把行号放入rowID rowIDs[i].push(g); //把每一行的内容放入dataBlob对象中 dataBlob[i+":"+g]=cars[g]; } } //更新状态 this.setState({ dataSource:this.state.dataSource.cloneWithRowsAndSections(dataBlob,sectionIDs,rowIDs) }) } }); const styles = StyleSheet.create({ outerView:{ flex:1 },headerView:{ height:64,backgroundColor:"orange",justifyContent:"center",alignItems:"center" },rowStyle:{ flexDirection:"row",alignItems:"center",padding:10,borderBottomColor:"#e8e8e8",borderBottomWidth:0.5 },rowImg:{ width:70,height:70,marginRight:10 },sectionHeader:{ backgroundColor:"#e8e8e8",height:30,justifyContent:"center" } }); export default ListViewDemo3;