ListView 高效数据列表 >=v1.0.0

最基本的使用方式就是创建一个ListView.DataSource数据源,然后给它传递一个普通的数据数组,再使用数据源来实例化一个ListView组件,并且定义它的renderRow回调函数,这个函数会接受数组中的每个数据作为参数,返回一个可渲染的组件(作为listview的每一行)。

注:该版本暂时不支持scrollTo。

属性

dataSource { ListViewDataSource }

源数据。

initialListSize { number }

指定在组件刚挂载的时候渲染多少行数据。用这个属性来确保首屏显示合适数量的数据,而不是花费太多帧逐步显示出来。

onChangeVisibleRows { function }

(visibleRows, changedRows) => void

当可见的行的集合变化的时候调用此回调函数。visibleRows 以 { sectionID: { rowID: true }}的格式包含了所有可见行,而changedRows 以{ sectionID: { rowID: true | false }}的格式包含了所有刚刚改变了可见性的行,其中如果值为true表示一个行变得可见,而为false表示行刚刚离开可视区域而变得不可见。

方法参数:

参数名 类型 描述 版本
visibleRows object 当前可见行
changedRows object 可见状态改变的行

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 通过调用该函数可通知ListView高亮该行

返回值: renderable 每行渲染组件

renderScrollComponent { function }

(props) => renderable

指定一个函数,在其中返回一个可以滚动的组件。ListView将会在该组件内部进行渲染。默认情况下会返回一个包含指定属性的ScrollView。

方法参数:

参数名 类型 描述 版本
可滚动组件的属性 object

返回值: renderable 可滚动组件

renderSeparator { function }

(sectionID, rowID, adjacentRowHighlighted) => renderable

如果提供了此属性,一个可渲染的组件会被渲染在每一行下面,除了小节标题的前面的最后一行。

方法参数:

参数名 类型 描述 版本
sectionID string 所处section名
rowID number 所处section中的index
adjacentRowHighlighted bool 邻近的行是否被高亮

返回值: renderable 分隔组件

scrollRenderAheadDistance { number }

当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行。

useOriginScrollView { bool }

如果提供了此属性, 会使用原生ScrollView,配置了renderScrollComponent时不生效。

scrollEventThrottle { bool }

触发onScroll最小间隔毫秒数,默认值为50。

方法

getScrollResponder

获取ScrollView,当使用原生ScrollView时(useOriginScrollView属性为true)返回滚动响应器。

返回值: scrollResponder ScrollView,当使用原生ScrollView时返回滚动响应器

startRefreshing

同ScrollView,当前组件有refreshControl属性,并且没有正在下拉刷新,则强制触发下拉刷新,变成正在刷新的状态。

stopRefreshing

同ScrollView,当前组件有refreshControl属性,并且正在下拉刷新,则停止下拉刷新的状态。

startLoading

同ScrollView,当前组件有loadControl属性,并且没有正在加载,则强制触发加载更多,变成正在加载更多的状态。

stopLoading

同ScrollView,当前组件有loadControl属性,并且正在加载,则停止加载更多的状态。

scrollToTop

返回顶部,可通过{animated: true}开启动画。可以参考这个例子

示例

import React, {
	Component,
	StyleSheet,
	View,
	Text,
	TouchableOpacity,
	Image,
	ListView,
	RefreshControl,
	LoadControl,
} from 'qunar-react-native'

// listview example
class ListViewExample extends Component {
    constructor(props){
        super(props)

        this.componentTimeouts = []

        const ds = new ListView.DataSource({
            rowHasChanged: (r1, r2) => r1 !== r2,
        })
        let rows = []
        for(let i = 0; i < 30; i++){
            rows.push(i)
        }
        this.state = {
            rows: rows,
        	dataSource: ds.cloneWithRows(rows),
        }
    }

    // 下拉刷新
    onRefresh() {
        this.componentTimeouts.push(setTimeout(() => {
            this.pushRows(10)
            this.refs.listview.stopRefreshing()
        }, 2000))
    }

    // 上拉加载
    onLoad() {
        this.componentTimeouts.push(setTimeout(() => {
            this.pushRows(20)
            this.refs.listview.stopLoading()
        }, 2000))
    }

    pushRows(number) {
        const ds = new ListView.DataSource({
            rowHasChanged: (r1, r2) => r1 !== r2,
        })

        let rows = this.state.rows,
            len = rows.length

        for(let i = 0; i < number; i++){
            rows.push(len + i)
        }

        this.setState({
            rows: rows,
            dataSource: ds.cloneWithRows(rows),
        })
    }

    render() {
        return (
            <View style={styles.container}>
                <ListView
                    ref="listview"
                    dataSource={this.state.dataSource}
                    renderRow={(rowData) => <ListViewExampleRow title={rowData}/>}
                    refreshControl={<RefreshControl onRefresh={this.onRefresh.bind(this)} />}
                    loadControl={
                        <LoadControl onLoad={() => this.onLoad()}
                            onPress={() => {
                                this.refs.listview.startLoading()
                                this.onLoad()
                            }}
                        />
                    }
                />
            </View>
        )
    }

    componentWillUnmount() {
        for(var timeout of this.componentTimeouts){
            clearTimeout(timeout)
        }
    }
}

// example row content
class ListViewExampleRow extends Component {
    constructor(props){
        super(props)
    }

    render() {
        const {title} = 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',
    }
})