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或者其它恰當的結構中。
頁腳會永遠在列表的最底部,而頁頭會在最頂部。
- 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
- 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', }, })