【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網格佈局效果




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章