ExperimentalListView 无限缓存列表 >= 1.5.0
在 QRN 中,列表组件有两大类使用场景。一种是行高可知(行高相同,或者只有几种固定类型的行高),并且有可能需要迅速跳转到某位置,比如城市列表。此时建议使用 InfiniteListView,它针对此种场景有特殊的优化。另一种则是行高未知,但一般只会按顺序渲染,不会跳到某个未渲染过的区域,此时可以使用 ListView 组件。由于它需要 JS 不断地获取每行渲染后的高度,以便计算之后内容该如何渲染,所以性能并不如前者。
最初 QRN 重写 ListView 是为了解决原有 React-Native 的 ListView 组件的内存使用问题(该问题至今未解决,参考: ListView renders all rows? 。但是经过一段时间的使用,发现重写后的组件由于 JS 与 Native 的频繁交互,和它的重用机制运算量比较大,使得渲染帧数并达不到预期。
目前改写了其内部机制,并强制控制了滚动的范围(防止出现渲染速度跟不上滚动速度造成白屏),由于改动较大,目前暂时作为单独的另一个 List 组件暴露出来。未来将会考虑替换现有的 ListView。
使用方式:
import {ExperimentalListView} from 'qunar-react-native'
props 与 methods 保持与原有 ListView 一致。
dataSource { ListViewDataSource }
源数据。
initialListSize { number }
指定在组件刚挂载的时候渲染多少行数据。用这个属性来确保首屏显示合适数量的数据,而不是花费太多帧逐步显示出来。
onEndReached { function }
当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。
onEndReachedThreshold { number }
调用onEndReached之前的临界值,单位是像素。
pageSize { number }
每次事件循环(每帧)渲染的行数。
renderFooter { function }
() => renderable
页头与页脚会在每次渲染过程中都重新渲染(如果提供了这些属性)。如果它们重绘的性能开销很大,可以考虑使用renderStaticHeader/renderStaticFooter。页脚会永远在列表的最底部,而页头会在最顶部。
返回值: renderable 页头组件
renderHeader { function }
() => renderable
同renderFooter,渲染页尾组件。
返回值: renderable 页尾组件
renderStaticFooter { function }
() => renderable
只渲染一次的头部组件。
返回值: renderable 页头组件
renderStaticHeader { function }
() => renderable
只渲染一次的尾部组件。
返回值: renderable 页尾组件
renderRow { function }
(rowData, sectionID, rowID, highlightRow) => renderable
从数据源(Data source)中接受一条数据,以及它和它所在section的ID。返回一个可渲染的组件来为这行数据进行渲染。默认情况下参数中的数据就是放进数据源中的数据本身,不过也可以提供一些转换器。
方法参数:
参数名 | 类型 | 描述 | 版本 |
---|---|---|---|
rowData | React.PropTypes.any | 数据源中的数据 | |
sectionID | string | 所处section名 | |
rowID | number | 所处section中的index | |
highlightRow | function | 通过调用该函数可通知ExperimentalListView高亮该行 |
返回值: renderable 每行渲染组件
renderScrollComponent { function }
(props) => renderable
指定一个函数,在其中返回一个可以滚动的组件。ExperimentalListView将会在该组件内部进行渲染。默认情况下会返回一个包含指定属性的ScrollView。
方法参数:
参数名 | 类型 | 描述 | 版本 |
---|---|---|---|
可滚动组件的属性 | object |
返回值: renderable 可滚动组件
scrollRenderAheadDistance { number }
当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行。
useOriginScrollView { bool }
如果提供了此属性,会使用原生ScrollView,配置了renderScrollComponent时不生效。
scrollEventThrottle { React.PropTypes.number }
触发onScroll最小间隔毫秒数,默认值为50。
configureRowHeight { React.PropTypes.func }
(rowData, rowID) => number 指定每一行的高度。
startRefreshing
同ScrollView,当前组件有refreshControl属性,并且没有正在下拉刷新,则强制触发下拉刷新,变成正在刷新的状态。
stopRefreshing
同ScrollView,当前组件有refreshControl属性,并且正在下拉刷新,则停止下拉刷新的状态。
startLoading
同ScrollView,当前组件有loadControl属性,并且没有正在加载,则强制触发加载更多,变成正在加载更多的状态。
stopLoading
同ScrollView,当前组件有loadControl属性,并且正在加载,则停止加载更多的状态。
scrollTo
同ScrollView ScrollTo。 TODO: 滚动到未渲染过的位置
scrollToTop
立即回到顶部,相当于scrollTo({y: 0})
getScrollResponder
获取ScrollView,当使用原生ScrollView时(useOriginScrollView属性为true)返回滚动响应器。
返回值: scrollResponder ScrollView,当使用原生ScrollView时返回滚动响应器
import React, {
Component,
StyleSheet,
View,
Text,
TouchableOpacity,
Image,
InfiniteListView,
} from 'qunar-react-native'
class InfiniteListViewExample extends Component {
constructor(props){
super(props)
const ds = new InfiniteListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
})
let rows = []
for(let i = 0; i < 200; i++){
rows.push(i)
}
this.state = {
rows: rows,
dataSource: ds.cloneWithRows(rows),
}
}
render() {
// 通过configureRowHeight配置不同行高
return (
<View style={styles.container}>
<InfiniteListView
ref="InfiniteListView"
dataSource={this.state.dataSource}
configureRowHeight={(rowData, rowIndex) => {return rowData % 10 === 0 ? 120 : 100}}
renderRow={(rowData) => <ListViewExampleRow title={rowData} text={this.state.text}/>}
/>
</View>
)
}
}
class ListViewExampleRow extends Component {
constructor(props){
super(props)
}
render() {
const {title, text} = this.props
const url = 'http://placeholdit.imgix.net/~text?txtsize=33&bg=666&txtclr=fff&txt=' + title + '&w=100&h=110',
randomText = Number(title) % 100
return (
<View style={styles.row}>
<Image
style={[styles.rowImg]}
source={{uri: 'http://placeholdit.imgix.net/~text?txtsize=33&bg=666&txtclr=fff&txt=' + title + '&w=100&h=110'}}
/>
<View style={[styles.rowContent]}>
<View style={{flexDirection: 'row'}}>
<Text style={styles.titleText}>{title}</Text>
</View>
<View>
<Text style={{color: '#25a4bb'}}>{randomText}分 / {randomText}条评论</Text>
<Text style={[styles.alignRight, {color: 'orange'}]}>{randomText}起</Text>
</View>
<View>
<View style={[styles.labelWrap]}>
<Text style={[styles.labelText]}>label{randomText}</Text>
<Text style={[styles.labelText]}>label{randomText}</Text>
<Text style={[styles.labelText]}>label{randomText}</Text>
</View>
<Text style={[styles.alignRight]}>{randomText}折vip</Text>
</View>
<View>
<Text style={styles.detailText}>地点:首都机场{randomText}</Text>
<TouchableOpacity style={[styles.alignRight]}>
<Text style={[styles.button]}>预定</Text>
</TouchableOpacity>
</View>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
row: {
flexDirection: 'row',
alignItems: 'center',
overflow: 'hidden',
},
rowImg: {
flex: 1,
height: 100,
resizeMode: 'stretch',
},
rowContent: {
flex: 3,
padding: 7,
},
titleText: {
fontWeight: 'bold',
fontSize: 14,
},
detailText: {
marginTop: 3,
color: '#999',
fontSize: 12,
},
alignRight: {
position: 'absolute',
top: 0,
right: 0,
},
button: {
padding: 2,
borderRadius: 4,
color: '#fff',
backgroundColor: '#09c',
},
labelWrap: {
flexDirection: 'row',
},
labelText: {
marginRight: 3,
borderRadius: 3,
borderWidth: 1,
borderColor: '#09c',
},
operationButton: {
padding: 5,
borderWidth: 1,
borderColor: '#069',
borderRadius: 5,
}
})
module.exports = {
title: 'VariousHeight',
examples: [{
render: () => {
return <InfiniteListViewExample/>
}
}]
};