【React Native开发】- ListView垂直的滚动列表

1.ListView介绍

ListView组件用于显示一个垂直滚动的列表,其中的元素之间结构相似,数据不同。

ListView适合于长列表数据,并且数据个数可添加及删除。与ScrollView不同的是Lisview不是渲染全部的数据,

而是优先渲染可见的数据。

ListView组件必不可少的两个属性是dataSource(列表数据源)和renderRow(逐个解析数据源中的数据,然后返回一个设置好格式的

组件进行渲染)。

ListView的一个常用场景就是从服务器端取回列表数据然后显示。

ListView最基本的实现方式是创建一个数据源 new ListView.DataSource(),然后给它传递一个数组类型的数据['rowData1','rowData2','rowData3'],然后在通过数据源实例化一个ListView组件,定义其renderRow回调函数,这个回调函数会接收数组数据中数据作为参数,返回一个可渲染的组件(ListView中的每一行数据)。

ListView还支持一些高级特性,如给每段/组(section)数据添加一个带有粘性的头部;在列表头部和尾部增加单独的内容;在到达列表尾部的时候调用回调函数(onEndReached),还有在视野内可见的数据变化时调用回调函数(onChangeVisibleRows),以及一些性能方面的优化。

有一些性能优化使得ListView可以滚动的更加平滑,尤其是在动态加载可能很大(或者概念上无限长的)数据集的时候:

  • 只更新变化的行 - 提供给数据源的rowHasChanged函数可以告诉ListView它是否需要重绘一行数据(即:数据是否发生了变化)参见ListViewDataSource
  • 限制频率的行渲染 - 默认情况下,每次消息循环只有一行会被渲染(可以用pageSize属性配置)。这把较大的工作分散成小的碎片,以降低因为渲染而导致丢帧的可能性。

index.Android.js和index.iOS.js中代码:

import './luancher'

luancher.js代码
import React, { Component } from 'react';
import {
    AppRegistry,
} from 'react-native';
import Root from './listView/root'
AppRegistry.registerComponent('ImageDemo', () => Root);

root.js代码:
import React,{Component} from 'react'
import
{
StyleSheet,
View,
Text,
ListView,
} from 'react-native'
export default class Root extends Component{
constructor(props){
super(props)
const dataSource = new ListView.DataSource({
rowHasChanged:(r1,r2) => (r1!==r2)
})
this.state={
dataSource
:dataSource.cloneWithRows([
'rowData1', 'rowData2', 'rowData3', 'rowData4', 'rowData5', 'rowData6', 'rowData7', 'rowData8'
])
}
}
render(){
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text style={styles.text}>{rowData}</Text>}>
</ListView>
</View>)
}
}
const
styles = StyleSheet.create({
container
:{
flex:1,
},
text
:{
textAlign:'center',
padding:5,
borderBottomWidth:1,
borderBottomColor:'#ccc',
marginHorizontal:10, },
})

2.ListView属性

 - dataSource: PropTypes.instanceOf(ListViewDataSource).isRequired,列表依赖的数据源。

 - initialListSize: PropTypes.number.isRequired,指定在组件刚挂载的时候渲染多少行数据。用这个属性来确保首屏显示合适数量的数据,而不是花费太多帧逐步显示出来。 
 
 - onChangeVisibleRows: React.PropTypes.func,(visibleRows, changedRows) => void      
  
  

当可见的行的集合变化的时候调用此回调函数。visibleRows 以 { sectionID: { rowID: true }}的格式包含了所有可见行,

changedRows 以{ sectionID: { rowID: true | false }}的格式包含了所有刚刚改变了可见性的行,其中如果值为true表示一个行变得可见,

而为false表示行刚刚离开可视区域而变得不可见。

 

- onEndReached: PropTypes.func,当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。

译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的),这个事件也会被触发,请自行做标记过滤。

 
 - onEndReachedThreshold: PropTypes.number.isRequired,调用onEndReached之前的临界值,单位是像素。
 
 - pageSize: PropTypes.number.isRequired,每次事件循环(每帧)渲染的行数。
 
 - removeClippedSubviews: React.PropTypes.bool,用于提升大列表的滚动性能。需要给行容器添加样式overflow:'hidden'。(Android已默认添加此样式)。此属性默认开启。
 
 - renderFooter: PropTypes.func,() => renderable      
       
       

页头与页脚会在每次渲染过程中都重新渲染(如果提供了这些属性)。如果它们重绘的性能开销很大,把他们包装到一个StaticContainer或者其它恰当的结构中。

页脚会永远在列表的最底部,而页头会在最顶部。

- renderHeader: PropTypes.func,
 
 - renderRow: PropTypes.func.isRequired,(rowData, sectionID, rowID, highlightRow) => renderable      
        
        

从数据源(Data source)中接受一条数据,以及它和它所在section的ID。返回一个可渲染的组件来为这行数据进行渲染。

默认情况下参数中的数据就是放进数据源中的数据本身,不过也可以提供一些转换器。如果某一行正在被高亮(通过调用highlightRow函数),

ListView会得到相应的通知。当一行被高亮时,其两侧的分割线会被隐藏。行的高亮状态可以通过调用highlightRow(null)来重置。

 

- renderScrollComponent: React.PropTypes.func.isRequired,(props) => renderable

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

 

- renderSectionHeader: PropTypes.func,(sectionData, sectionID) => renderable

如果提供了此函数,会为每个小节(section)渲染一个粘性的标题。粘性是指当它刚出现时,会处在对应小节的内容顶部;继续下滑当它到达屏幕顶端的时候,

它会停留在屏幕顶端,一直到对应的位置被下一个小节的标题占据为止。可以使用 stickySectionHeadersEnabled来决定是否启用其粘性特性。

 

- renderSeparator: PropTypes.func,(sectionID, rowID, adjacentRowHighlighted) => renderable

如果提供了此属性,一个可渲染的组件会被渲染在每一行下面,除了小节标题的前面的最后一行。 在其上方的小节ID和行ID,以及邻近的行是否被高亮会作为参数传递进来。
 
 - scrollRenderAheadDistance: React.PropTypes.number.isRequired,当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行。
 
 - stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number).isRequired,一个子视图下标的数组,用于决定哪些成员会在滚动之后固定在屏幕顶端。
 举个例子,传递stickyHeaderIndices={[0]}会让第一个成员固定在滚动视图顶端。这个属性不能和horizontal={true}一起使用。

3.方法

 - getMetrics: function() 导出一些用于性能分析的数据。
 
 - scrollTo: function(...args: Array<mixed>) 滚动到指定的x, y偏移处,可以指定是否加上过渡动画。
 
 - scrollToEnd: function(options?: { animated?: boolean }) 滚动到视图底部(水平方向的视图则滚动到最右边)。
             
             

加上动画参数 scrollToEnd({animated: true})则启用平滑滚动动画,或是调用scrollToEnd({animated: false})来立即跳转。如果不使用参数,则animated选项默认启用。

4.实例

4.1.1图文排列实例

import React,{Component} from 'react'
import {
    StyleSheet,
    View,
    Text,
    Image,
    ListView,
    TouchableHighlight,
    ToastAndroid,
} from 'react-native'
export default class Get extends Component{
    constructor(props){
        super(props)
        const dataSource = new ListView.DataSource({
            rowHasChanged:((r1,r2) => (r1 !==r2))
        })
        this.state = {
            dataSource:dataSource
        }
    }
    render(){
        return (
            <View style={styles.container}>
                <ListView
                    dataSource={this.state.dataSource}
                    initialListSize={10}
                    renderRow={this._renderRow.bind(this)}>
                    
                </ListView>
            </View>
        )
    }
    componentDidMount(){
        this.getData()
    }
    getData(){
        fetch('http://gank.io/api/data/%E7%A6%8F%E5%88%A9/10/2',{
            method:'GET'
        }).then((response) => response.json())//获取数据
            .then((responseText) => {//处理数据
                this.setState({
                    dataSource:this.state.dataSource.cloneWithRows(responseText.results)
                })
            })
            .catch((error) => {
                console.warn(error)
            }).done()
    }
    _renderRow(rowData:string,sectionID:number,rowID:number){
        return (
            <TouchableHighlight 
                style={styles.touchStyle} 
                onPress={this.showToast.bind(this,rowData)}//只有设置了点击事件,背景颜色和透明度才有效果
                activeOpacity={0.8}//触摸时透明度
                underlayColor='#eee'//点击时背景阴影效果的背景颜色
            >
                <View style={styles.item}>
                    <Image style={styles.itemImg} source={{uri:rowData.url}}></Image>
                    <Text style={styles.itemText}>{rowData.publishedAt}</Text>
                </View>
            </TouchableHighlight>
        )
    }
    showToast(rowData){
        ToastAndroid.show(rowData.publishedAt,1000)
    }
}
const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    touchStyle:{
        height:80,
    },
    item:{
        flexDirection:'row',
        justifyContent:'space-around',
        alignItems:'center',
        borderBottomWidth:1,
        borderBottomColor:'#ccc',
        marginHorizontal:10,
        height:80,

    },
    itemText:{
        fontSize:16,
    },
    itemImg:{
        width:60,
        height:70,
    },
})

4.1.2图文效果

4.2.1网格布局实例

import React,{Component} from 'react'
import {
    StyleSheet,
    View,
    Text,
    ListView,
    Image,
    TouchableHighlight,
} from 'react-native'
const THUMB_URLS = [
    {uri:"http://www.qq745.com/uploads/allimg/150408/1-15040PJ142-50.jpg"},
    {uri:"http://ww2.sinaimg.cn/large/8989048bjw1dutawvaz66j.jpg"},
    {uri:"http://img.lenovomm.com/s3/img/app/app-img-lestore/1993-2015-07-14062642-1436869602788.jpg?isCompress=true&width=342&height=513&quantity=0.8&rotate=true&dk=2"},
    {uri:"http://c.hiphotos.baidu.com/exp/whcrop=160,120/sign=57e0ac939058d109c4b6fff0be28f18e/b8389b504fc2d5620f811f00e51190ef77c66c56.jpg"},
    {uri:"http://f.hiphotos.baidu.com/exp/whcrop=160,120/sign=c3918fdeba014a90816b10ffc6070423/34fae6cd7b899e51d2350a7841a7d933c8950d26.jpg"},
    {uri:"http://f.hiphotos.baidu.com/exp/whcrop=160,120/sign=2aba0e6674c6a7efb973fe64928a9260/902397dda144ad3494292c3ed2a20cf430ad85f1.jpg"},
    {uri:"http://v1.qzone.cc/avatar/201503/04/17/44/54f6d3f8ae76c662.jpg%21200x200.jpg"},
    {uri:"http://awb.img.xmtbang.com/img/uploadnew/201510/22/8d822cf398d1482fbe3d0ac6208050c4.jpg"},
    {uri:"http://awb.img1.xmtbang.com/wechatmsg2015/article201506/20150601/thumb/6abcaf1a9c69496b8d51ec13eabfb5dd.jpg"},
    {uri:"http://imgsrc.baidu.com/forum/w%3D580/sign=7eb05e9eddf9d72a17641015e428282a/3e87194c510fd9f9b3d66fc8212dd42a2a34a4c9.jpg"},
];
export default class Root extends Component{
    constructor(props){
        super(props)
        const dataSource = new ListView.DataSource({
            rowHasChanged:(r1,r2) => (r1!==r2)
        })
        var datas = [];
        for (var i = 0; i < 10; i++) {

            datas.push('Row ' + i );
        }
        this.state={
            dataSource:dataSource.cloneWithRows(datas)
        }
    }
    render(){
        return (
            <View style={styles.container}>
                <ListView
                    contentContainerStyle={styles.listStyle}
                    dataSource={this.state.dataSource}//数据源
                    initialListSize={10}//指定组件在刚挂载时渲染的行数。
                    renderRow={this._renderRow.bind(this)}
                >
                </ListView>
            </View>
        )
    }
    _renderRow(rowData:string,sectionID:number,rowID:number){
        const imageSource = THUMB_URLS[rowID]
        return (
            <TouchableHighlight style={styles.touchBtn}>
                <View style={styles.itemStyle}>
                    <Image style={styles.thumb} source={imageSource} ></Image>
                    <Text style={styles.text}>{rowData}</Text>
                </View>
            </TouchableHighlight>
        )
    }
}
const styles = StyleSheet.create({
    container:{
        flex:1,
    },
    text:{
        fontSize:16,
    },
    touchBtn:{
        height:160,
    },
    itemStyle:{
        justifyContent:'center',
        padding:5,
        margin:3,
        width:150,
        height:150,
        backgroundColor:'#F6F6F6',
        alignItems:'center',
        borderWidth:1,
        borderColor:'#cccccc',
        borderRadius:5,
    },
    thumb:{
        marginTop:5,
        width:110,
        height:120,
    },
    listStyle:{
        marginTop:5,
        flexDirection:'row',
        flexWrap:'wrap',
        justifyContent:'space-around',
    },
})

4.2.1网格布局效果




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值