点击查看文档
ListView常用的属性
ScrollView 相关属性样式全部继承
dataSource ListViewDataSource 设置ListView的数据源
initialListSize number
设置ListView组件刚刚加载的时候渲染的列表行数,用这个属性确定首屏或者首页加载的数量,而不是花大量的时间渲染加载很多页面数据,提高性能。onChangeVisibleRows function
(visibleRows,changedRows)=>void。
当可见的行发生变化的时候回调该方法。onEndReachedThreshold number
当偏移量达到设置的临界值调用onEndReachedonEndReached function
当所有的数据项行被渲染之后,并且列表往下进行滚动。一直滚动到距离底部onEndReachedThredshold设置的值进行回调该方法。原生的滚动事件进行传递(通过参数的形式)。pageSize number 每一次事件的循环渲染的行数
removeClippedSubviews bool
该属性用于提供大数据列表的滚动性能。该使用的时候需要给每一行(row)的布局添加over:'hidden'样式。该属性默认是开启状态。renderFooter function 方法 ()=>renderable
在每次渲染过程中头和尾总会重新进行渲染。如果发现该重新绘制的性能开销比较大的时候,可以使用StaticContainer容器或者其他合适的组件。renderHeader function 方法
在每一次渲染过程中Footer(尾)该会一直在列表的底部,header(头)该会一直在列表的头部,用法同上。renderRow function (rowData,sectionID,rowID,highlightRow)=>renderable
该方法有四个参数,其中分别为数据源中一条数据,分组的ID,行的ID,以及标记是否是高亮选中的状态信息。renderScrollComponent function 方法 (props)=>renderable
该方法可以返回一个可以滚动的组件。默认该会返回一个ScrollViewrenderSectionHeader function (sectionData,sectionID)=>renderable
如果设置了该方法,这样会为每一个section渲染一个粘性的header视图。该视图粘性的效果是当刚刚被渲染开始的时候,该会处于对应的内容的顶部,然后开始滑动的时候,该会跑到屏幕的顶端。直到滑动到下一个section的header(头)视图,然后被替代为止。renderSeparator function (sectionID,rowID,adjacentRowHighlighted)=>renderable
如果设置该方法,会在被每一行的下面渲染一个组件作为分隔。除了每一个section分组的头部视图前面的最后一行。scrollRenderAheadDistance number
进行设置当该行进入屏幕多少像素以内之后就开始渲染该行
ListView的高阶特性
ListView同样支持一些高级特性,包括设置每一组的粘性的头部(类似于iPhone)、支持设置列表的header以及footer视图、当数据列表滑动到最底部的时候支持onEndReached方法回调、设备屏幕列表可见的视图数据发生变化的时候回调onChangeVisibleRows以及一些性能方面的优化特性。
-
ListView设计的时候,当需要动态加载非常大的数据的时候,下面有一些方法性能优化的方法可以让我们的ListView滚动的时候更加平滑:
- 只更新渲染数据变化的那一行 ,rowHasChanged方法会告诉ListView组件是否需要重新渲染当前那一行。
- 选择渲染的频率,默认情况下面每一个event-loop(事件循环)只会渲染一行(可以同pageSize自定义属性设置)。这样可以把大的工作量进行分隔,提供整体渲染的性能。
实例代码
最简单的ListView
- 运行效果如下:
代码:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
PixelRatio,
TouchableOpacity
} from 'react-native';
// 引入本地的数据
const wineArr = require('./Wine.json');
export default class ListViewDemo extends Component {
// 构造
constructor(props) {
super(props);
// 1.创建数据源
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
// 初始状态
this.state = {
dataSource: ds.cloneWithRows([''])
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
);
}
componentDidMount() {
this.setState({
dataSource:this.state.dataSource.cloneWithRows(wineArr)
})
}
_renderRow(rowData, sectionID, rowID){
return(
<TouchableOpacity
style={styles.cellViewStyle}
onPress={()=>alert('点击了第' + sectionID + '组中的第' + rowID + '行')}
>
{/*左边*/}
<Image source={{uri: rowData.image}} style={styles.cellImgStyle}/>
{/*右边*/}
<View style={styles.rightViewStyle}>
<Text
style={styles.mainTitleStyle}
numberOfLines={2}
>
{rowData.name}
</Text>
<Text style={styles.subTitleStyle}>¥{rowData.money}.00</Text>
</View>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
cellViewStyle:{
borderBottomWidth: 1 / PixelRatio.get(),
borderBottomColor:'#ccc',
/*主轴的方向*/
flexDirection:'row',
padding:10
},
cellImgStyle:{
width: 90,
height: 60,
resizeMode:'contain'
},
rightViewStyle:{
flex:1,
// backgroundColor:'red',
justifyContent:'space-between'
},
mainTitleStyle:{
fontSize: 15,
color:'red'
},
subTitleStyle:{
color:'#999'
}
});
AppRegistry.registerComponent('ListViewDemo', () => ListViewDemo);
ListView的吸顶效果:
点击下载Demo
- 效果图:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
TouchableOpacity,
} from 'react-native';
// 引入JSON数据
var CarData = require('./Car.json');
// 屏幕宽度
var Dimensions = require('Dimensions');
var {width} = Dimensions.get('window');
export default class ListSectionView extends Component {
// 构造
constructor(props) {
super(props);
//获取组的数据
var getSectionData = (dataBlob, sectionID)=> {
return dataBlob[sectionID];
};
// 获取行数据
var getRowData = (dataBlob, sectionID,rowID) => {
return dataBlob[sectionID + ':' + rowID];
};
// 设置初始化状态
this.state = {
dataSource: new ListView.DataSource({
getSectionData: getSectionData,
getRowData: getRowData,
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
})
};
}
render() {
return (
<View style={styles.outViewStyle}>
<View style={styles.TopViewStyle}>
<Text style={{fontSize:30,color:'white'}}>汽车品牌</Text>
</View>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderSectionHeader={this._renderSection}
/>
</View>
);
}
// 耗时操作 数据请求。
componentDidMount() {
{
this.setDataSources()
}
}
setDataSources() {
// 获得JSON数据数组
const carArray = CarData.data;
var dataBlob = {}, sectionIDs = [], rowIDs = [], Cars = [];
//遍历获取section的下标和内容
for (var i = 0; i < carArray.length; i++) {
// 将每组的title存入到dataBlob中
dataBlob[i] = carArray[i].title;
// 将组号存入sectionIDs中
sectionIDs.push(i);
//将每组中有多少行数组
rowIDs[i] = [];
// 取出每一组中的行的内容
Cars = carArray[i].cars;
for (var j = 0; j < Cars.length; j++) {
//将行号存入数组中
rowIDs[i].push(j);
// 将每行的内容存入到dataBlob中
dataBlob[i + ':' + j] = Cars[j];
}
}
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
});
}
//每一行数据
_renderRow(rowData) {
return (
<TouchableOpacity activeOpacity={0.5}>
<View style={styles.cellStyles}>
<Image source={{uri:rowData.icon}} style={styles.imageStyles}/>
<Text style={{fontSize:30}}>{rowData.name}</Text>
</View>
</TouchableOpacity>
)
}
//每一组数据
_renderSection(sectionData){
return (
<View style={styles.sectionHeaderStyle}>
<Text style={{fontSize:20,color:'red'}}>{sectionData}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
outViewStyle:{
flex:1,
},
TopViewStyle:{
width:width,
height:80,
justifyContent:'center',
alignItems:'center',
backgroundColor:'orange'
},
sectionHeaderStyle:{
backgroundColor:'#ccc',
height:25,
// alignItems:'center',
justifyContent:'center'
},
cellStyles:{
flexDirection:'row',
borderBottomColor:'#ccc',
borderBottomWidth:1,
padding:10,
alignItems:'center'
},
imageStyles:{
width: 90,
height:90,
marginRight:10
},
});
AppRegistry.registerComponent('ListSectionView', () => ListSectionView);
-
案例技术点分析:
- 在React Native中,ScrollView组件可以使用 stickyHeaderIndices 轻松实现 sticky 效果;而使用ListView组件时,使用 stickyHeaderIndices 则不生效。
- 如何实现滚动时每个section header会吸顶?
- 在
ListView
中要实现sticky
,需要使用cloneWithRowsAndSections
方法,将dataBlob(object)
,sectionIDs (array)
,rowIDs (array)
三个值传进去。
dataBlob object类型
sectionIDs array娄型
rowIDs array类型
-
dataBlob
dataBlob
包含ListView
所需的所有数据(section header 和 rows),在ListView渲染数据时,使用getSectionData
和 getRowData
来渲染每一行数据。 dataBlob
的 key
值包含 sectionID + rowId
-
sectionIDs
sectionIDs 用于标识每组section。
-
rowIDs
rowIDs 用于描述每个 section 里的每行数据的位置及是否需要渲染。在ListView渲染时,会先遍历 rowIDs 获取到对应的 dataBlob 数据。
九宫格案例
通常情况下,我们对ListView
的操作是纵向的,如果是横向的,则需要设置ListView
的contentContainerStyle
属性,添加flexDirection:‘row’
让多个ListView
在同一行显示,而且通过flexWrap:'wrap'
进行换行。
- 运行效果:
案例代码:
Demo下载
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
TouchableOpacity,
AlertIOS,
} from 'react-native';
const shareData = require('./shareData.json').data;
//计算
var Dimensions = require('Dimensions');
var {width} = Dimensions.get('window');
var cellWH = 100;
var clos = 3;
var VMargin = (width - clos * cellWH) / (clos + 1);
export default class ListViewShare extends Component {
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2})
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
contentContainerStyle={styles.contentContainerStyle}
/>
);
}
componentDidMount() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(shareData)
})
}
_renderRow(rowData){
return(
<TouchableOpacity activeOpacity={0.5} onPress={()=>{alert(rowData.title)}}>
<View style={styles.cellStyles}>
<Image source={{uri:rowData.icon}} style={styles.imageStyles}/>
<Text>{rowData.title} </Text>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
contentContainerStyle: {
flexDirection:'row',
flexWrap:'wrap',
alignItems:'center',
},
cellStyles: {
width:cellWH,
height:cellWH,
marginLeft:VMargin,
marginTop:VMargin,
alignItems:'center',
},
imageStyles: {
width:80,
height:80,
resizeMode:'contain',
},
});
AppRegistry.registerComponent('ListViewShare', () => ListViewShare);