跨平台开发RN-组件ListView
组件ListView
- ListView就是iOS中的UITabelView
- 使用步骤:
- 由于ListView在0.60之后已经过期了,被移除
react-native
框架了,因此导入方法为:import ListView from "deprecated-react-native-listview";
- 创建一个
ListView.DataSource
数据源,然后给它传递一个普通的数据数组; - 使用数据源(
data source
)实例化一个ListView
组件,定义一个renderRow
回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。
- 由于ListView在0.60之后已经过期了,被移除
-
代码如下:
import React, {Component} from 'react'; import {View, StyleSheet, Image, Text} from 'react-native'; import ListView from 'deprecated-react-native-listview'; //导入包的json数据:[{image: money: name:},{...}] //是一个数组 var wineData = require('./resources/Wine'); class Enty6 extends Component { //1.创建一个 ListView.DataSource 数据源,然后给它传递一个普通的数据数组; constructor(props) { super(props); //创建数据源 var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 != r2}); //设置数据 this.state = { dataSource: ds.cloneWithRows(wineData), }; } //2. 使用数据源(data source)实例化一个ListView组件,定义一个renderRow回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 render() { return ( <ListView //使用数据源(`data source`)实例化一个`ListView`组件 dataSource={this.state.dataSource} //定义一个`renderRow`回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 renderRow={this.renderRow} /> ); } //返回具体的cell,相当于iOS的-(UITableViewCell)tableView...数据源方法 renderRow(rowData, sectionID, rowID, highlightRow) { console.log(sectionID); //返回要实现的cell return ( <View> <Text>1111</Text> </View> ); } }
ListView常用属性
- ScrollView 相关属性样式全部继承
- dataSource: ListViewDataSource 设置ListView的数据源
- initialListSize:number
- 设置ListView组件刚刚加载的时候渲染的列表行数,用这个属性确定首屏或者首页加载的数量,而不是花大量的时间渲染加载很多页面数据,提高性能。
- onChangeVisibleRows: function (visibleRows,changedRows)=>void。
- 当可见的行发生变化的时候回调该方法。
- onEndReachedThreshold: number
- 当偏移量达到设置的临界值调用onEndReached
- onEndReached 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
- 该方法可以返回一个可以滚动的组件。默认该会返回一个ScrollView
- renderSectionHeader 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使用案例
案例1,图文列表
-
效果如下
-
代码如下:
import React, {Component} from 'react'; import { View, StyleSheet, Image, Text, Dimensions, TouchableOpacity, Alert, } from 'react-native'; import ListView from 'deprecated-react-native-listview'; //导入包的json数据:[{image: money: name:},{...}] //是一个数组 var wineData = require('./resources/Wine'); var screenW = Dimensions.get('window').width; class Enty6 extends Component { //1.创建一个 ListView.DataSource 数据源,然后给它传递一个普通的数据数组; constructor(props) { super(props); //创建数据源 //当且仅当任意的2个cell内容完全不相等时,才会重新创建cell,相当于iOS的复用功能 var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 != r2}); //设置数据 this.state = { dataSource: ds.cloneWithRows(wineData), }; } //2. 使用数据源(data source)实例化一个ListView组件,定义一个renderRow回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 render() { return ( <ListView //使用数据源(`data source`)实例化一个`ListView`组件 dataSource={this.state.dataSource} //定义一个`renderRow`回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 renderRow={this.renderRow} /> ); } //3. 返回具体的cell,相当于iOS的-(UITableViewCell)tableView...数据源方法 renderRow(rowData, sectionID, rowID, highlightRow) { //返回要实现的cell return ( //给cell添加点击事件 <TouchableOpacity activeOpacity={0.5} onPress={() => { Alert.alert('点击了' + rowID + '行'); }}> <View style={styles.cellViewStyle}> {/*左边的图片*/} <Image source={*uri: rowData.image}} style={styles.leftImageStyle} /> {/*右边的View*/} <View style={styles.rightViewStyle}> {/*上边的文本信息*/} <Text style={styles.topTitleStyle}>{rowData.name}</Text> {/*下边的文本信息*/} <Text style={styles.bottomTitleStyle}>¥{rowData.money}</Text> </View> </View> </TouchableOpacity> ); } } const styles = StyleSheet.create({ cellViewStyle: { padding: 10, backgroundColor: 'white', //cell下划线 borderBottomWidth: 0.5, borderBottomColor: '#e8e8e8', //设置主轴方向 flexDirection: 'row', }, leftImageStyle: { width: 60, height: 60, //举例右边举例 marginRight: 15, }, rightViewStyle: { justifyContent: 'center', }, topTitleStyle: { color: 'red', fontSize: 15, // 自动换行 width: screenW - 100, }, bottomTitleStyle: { color: 'blue', marginTop: 8, }, }); export default Enty6;
案例2:ListView 实现九宫格
- 效果图
- 技术点
- 通常情况下,我们对ListView的操作是纵向的,如果是横向的,则需要设置
ListView
的contentContainerStyle
属性,添加flexDirection:‘row’
让多个ListView
在同一行显示,而且通过flexWrap:'wrap'
进行换行。
- 通常情况下,我们对ListView的操作是纵向的,如果是横向的,则需要设置
-
代码实现:
import React, {Component} from 'react'; import { View, StyleSheet, Image, Text, Dimensions, TouchableOpacity, Alert, } from 'react-native'; import ListView from 'deprecated-react-native-listview'; //导入包的json数据:{data: [{icon: title:},{...}]} //是一个数组 var shareData = require('./resources/shareData'); var screenW = Dimensions.get('window').width; var clos = 3; //每个cell的宽高 var itemW = 80; var itemH = itemW + 16; //水平间距 var vmargin = (screenW - itemW * clos) / (clos + 1); //垂直间距 var hmargin = 20; class Enty7 extends Component { //1.创建一个 ListView.DataSource 数据源,然后给它传递一个普通的数据数组; constructor(props) { super(props); //创建数据源 //当且仅当任意的2个cell内容完全不相等时,才会重新创建cell,相当于iOS的复用功能 var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 != r2}); //设置数据 this.state = { dataSource: ds.cloneWithRows(shareData.data), }; } //2. 使用数据源(data source)实例化一个ListView组件,定义一个renderRow回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 render() { return ( <ListView //使用数据源(`data source`)实例化一个`ListView`组件 dataSource={this.state.dataSource} //定义一个`renderRow`回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 renderRow={this.renderRow} //修改ListView的contentView的显示方式 contentContainerStyle={styles.listViewStyle} /> ); } //3. 返回具体的cell,相当于iOS的-(UITableViewCell)tableView...数据源方法 renderRow(rowData, sectionID, rowID, highlightRow) { //返回要实现的cell return ( <TouchableOpacity activeOpacity={0.5} onPress={() => { Alert.alert('点击了' + rowID + '行'); }}> <View style={styles.cellViewStyle}> <Image source={*uri: rowData.icon}} style={styles.imageStyle} /> {/*不能直接写rowData.title,因为这么写他是一个常量,加{}就是变量了*/} <Text>{rowData.title}</Text> </View> </TouchableOpacity> ); } } const styles = StyleSheet.create({ imageStyle: { width: 50, height: 50, marginBottom: 5, }, //修改ListView内容排列方式 listViewStyle: { //修改主轴方向,这cell横向排列 flexDirection: 'row', //让cells换行显示 flexWrap: 'wrap', }, cellViewStyle: { backgroundColor: 'red', width: itemW, //图标+标题 height: itemH, marginLeft: vmargin, marginTop: hmargin, //内容居中 justifyContent: 'center', alignItems: 'center', }, }); export default Enty7;
案例3:ListView分组展示
- 技术点分析:
- 在React Native中,ScrollView组件可以使用
stickyHeaderIndices
轻松实现sticky
(吸顶)效果;而使用ListView组件时,使用stickyHeaderIndices
则不生效。 - 如何实现滚动时每个section header会吸顶?
- 在ListView中要实现
sticky
,需要使用cloneWithRowsAndSections
方法,将dataBlob(object),sectionIDs(array),rowIDs(array)三个值传进去。
- 在ListView中要实现
- 那么dataBlob这3个数据是什么呢?
-
先看源数据
{ "data": [ { "cars": [ { "icon": "m_180_100.png", "name": "AC Schnitzer" }, { "icon": "m_92_100.png", "name": "阿尔法·罗密欧" }, { "icon": "m_9_100.png", "name": "奥迪" } ], "title": "A" }, { "cars": [ { "icon": "m_172_100.png", "name": "巴博斯" }, { "icon": "m_157_100.png", "name": "宝骏" } ], "title": "B" }, { "cars": [ { "icon": "m_113_100.png", "name": "道奇" }, { "icon": "m_165_100.png", "name": "大通" } ], "title": "C" }, ... ] }
- dataBlob数据
- 包含ListView所需的所有数据(section header 和 rows),在ListView渲染数据时,使用
getSectionData
和getRowData
来渲染每一行数据。 -
数据如下:
[ // 组ID : title {"0":"A"}, {"1":"B"}, {"2":"C"}, ... //组ID:行ID : 行value { "0:0" : { "icon": "m_180_100.png", "name": "AC Schnitzer" } }, { "0:1" : { "icon": "m_92_100.png", "name": "阿尔法·罗密欧" } }, { "0:2" : { "icon": "m_9_100.png", "name": "奥迪" } }, { "1:0" : { "icon": "m_172_100.png", "name": "巴博斯" } }, { "1:1" : { "icon": "m_157_100.png", "name": "宝骏" } }, { "2:0" : { "icon": "m_113_100.png", "name": "道奇"" } }, { "3:1" : { "icon": "m_165_100.png", "name": "大通" } }, ... ]
- 即数据格式为:将一维多key字典数组整合成一维单key字典数组,key值为组ID和组ID:行ID
- 包含ListView所需的所有数据(section header 和 rows),在ListView渲染数据时,使用
- sectionIDs
- 就是一个数组存放所有的组ID
- 作用:
dataBlob[sectionID]
取数据
- rowIDs
-
是一个二维数组,外层长度为Section长度,内层长度为sectionID对应的row长度
[ [rowID0,rowID1,rowID2...], [rowID0,rowID1,rowID2...], ... ]
-
-
- 在React Native中,ScrollView组件可以使用
-
效果图如下:
-
代码示例:
import React, {Component} from 'react'; import { View, StyleSheet, Image, Text, Dimensions, TouchableOpacity, Alert, } from 'react-native'; import ListView from 'deprecated-react-native-listview'; //导入车的json数据:{data: [{cars:[{icon: name: },...] title:},{...}]} //是一个数组 var carData = require('./resources/Car'); var screenW = Dimensions.get('window').width; class Enty8 extends Component { //1.创建一个 ListView.DataSource 数据源,然后给它传递一个普通的数据数组; constructor(props) { super(props); //根据sectionID获取组数据 var getSectionData = (dataBlob, sectionID) => { return dataBlob[sectionID]; }; //根据sectionID与rowID获取行数据 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, }), }; } //2. 使用数据源(data source)实例化一个ListView组件,定义一个renderRow回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 render() { return ( <View style={styles.outViewStyle}> <View style={styles.headerViewStyle}> <Text style={*color: 'white', fontSize: 25}}>hhhhhhhh</Text> </View> <ListView //使用数据源(`data source`)实例化一个`ListView`组件 dataSource={this.state.dataSource} //定义一个`renderRow`回调函数,这个函数会接受数组中的每个数据作为参数,并返回一个可渲染的组件(该就是列表的每一行的item)。 renderRow={this.renderRow} renderSectionHeader={this.renderSectionHeader} /> </View> ); } //复杂操作,数据请求,异步操作 componentDidMount() { this.loadDataFromJson(); } //3. 返回具体的cell,相当于iOS的-(UITableViewCell)tableView...数据源方法 renderRow(rowData, sectionID, rowID) { //返回要实现的cell return ( <TouchableOpacity activeOpacity={0.5} onPress={() => { Alert.alert('点击了' + rowID + '行'); }}> <View style={styles.rowViewStyle}> <Image source={*uri: rowData.icon}} style={styles.imageStyle} /> <Text style={*marginLeft: 5}}>{rowData.name}</Text> </View> </TouchableOpacity> ); } //返回section的视图控件 renderSectionHeader(sectionData, sectionID) { return ( <View style={styles.sectionHeaderStyle}> <Text style={*marginLeft: 15, color: 'red', marginTop: 5}}> {sectionData} </Text> </View> ); } //整理数据 //{data: [{cars:[{icon: name: },...] title:},{...}]} loadDataFromJson() { //拿到json数据 var jsonData = carData.data; //定义一些变量 var dataBlob = []; var sectionIDs = []; var rowIDs = []; var rcars = []; for (var i = 0; i < jsonData.length; i++) { //1. 把组号放入sectionIDs sectionIDs.push(i); //2. 把组中内容放入dataBlob中 dataBlob[i] = jsonData[i].title; //3. 取出改组中所有的car rcars = jsonData[i].cars; rowIDs[i] = []; //4. 遍历所有的car for (var j = 0; j < rcars.length; j++) { //把行号放入rowIDs rowIDs[i].push(j); //把每一行的内容放入dataBlob中 dataBlob[i + ':' + j] = rcars[j]; } } //更新状态 this.setState({ dataSource: this.state.dataSource.cloneWithRowsAndSections( dataBlob, sectionIDs, rowIDs, ), }); } } const styles = StyleSheet.create({ outViewStyle: { //占满整个窗口 flex: 1, }, imageStyle: { width: 50, height: 50, }, headerViewStyle: { height: 64, backgroundColor: 'orange', justifyContent: 'center', alignItems: 'center', }, sectionHeaderStyle: { backgroundColor: '#e8e8e8', height: 25, }, rowViewStyle: { flexDirection: 'row', alignItems: 'center', padding: 10, borderBottomWidth: 1, borderBottomColor: '#e8e8e8', }, }); export default Enty8;