點擊查看文檔
ListView常用的屬性
ScrollView 相關屬性樣式全部繼承
dataSource ListViewDataSource 設置ListView的數(shù)據(jù)源
initialListSize number
設置ListView組件剛剛加載的時候渲染的列表行數(shù),用這個屬性確定首屏或者首頁加載的數(shù)量,而不是花大量的時間渲染加載很多頁面數(shù)據(jù),提高性能。onChangeVisibleRows function
(visibleRows,changedRows)=>void。
當可見的行發(fā)生變化的時候回調(diào)該方法。onEndReachedThreshold number
當偏移量達到設置的臨界值調(diào)用onEndReachedonEndReached function
當所有的數(shù)據(jù)項行被渲染之后,并且列表往下進行滾動。一直滾動到距離底部onEndReachedThredshold設置的值進行回調(diào)該方法。原生的滾動事件進行傳遞(通過參數(shù)的形式)。pageSize number 每一次事件的循環(huán)渲染的行數(shù)
removeClippedSubviews bool
該屬性用于提供大數(shù)據(jù)列表的滾動性能。該使用的時候需要給每一行(row)的布局添加over:'hidden'樣式。該屬性默認是開啟狀態(tài)。renderFooter function 方法 ()=>renderable
在每次渲染過程中頭和尾總會重新進行渲染。如果發(fā)現(xiàn)該重新繪制的性能開銷比較大的時候,可以使用StaticContainer容器或者其他合適的組件。renderHeader function 方法
在每一次渲染過程中Footer(尾)該會一直在列表的底部,header(頭)該會一直在列表的頭部,用法同上。renderRow function (rowData,sectionID,rowID,highlightRow)=>renderable
該方法有四個參數(shù),其中分別為數(shù)據(jù)源中一條數(shù)據(jù),分組的ID,行的ID,以及標記是否是高亮選中的狀態(tài)信息。renderScrollComponent function 方法 (props)=>renderable
該方法可以返回一個可以滾動的組件。默認該會返回一個ScrollViewrenderSectionHeader function (sectionData,sectionID)=>renderable
如果設置了該方法,這樣會為每一個section渲染一個粘性的header視圖。該視圖粘性的效果是當剛剛被渲染開始的時候,該會處于對應的內(nèi)容的頂部,然后開始滑動的時候,該會跑到屏幕的頂端。直到滑動到下一個section的header(頭)視圖,然后被替代為止。renderSeparator function (sectionID,rowID,adjacentRowHighlighted)=>renderable
如果設置該方法,會在被每一行的下面渲染一個組件作為分隔。除了每一個section分組的頭部視圖前面的最后一行。scrollRenderAheadDistance number
進行設置當該行進入屏幕多少像素以內(nèi)之后就開始渲染該行
ListView的高階特性
ListView同樣支持一些高級特性,包括設置每一組的粘性的頭部(類似于iPhone)、支持設置列表的header以及footer視圖、當數(shù)據(jù)列表滑動到最底部的時候支持onEndReached方法回調(diào)、設備屏幕列表可見的視圖數(shù)據(jù)發(fā)生變化的時候回調(diào)onChangeVisibleRows以及一些性能方面的優(yōu)化特性。
-
ListView設計的時候,當需要動態(tài)加載非常大的數(shù)據(jù)的時候,下面有一些方法性能優(yōu)化的方法可以讓我們的ListView滾動的時候更加平滑:
- 只更新渲染數(shù)據(jù)變化的那一行 ,rowHasChanged方法會告訴ListView組件是否需要重新渲染當前那一行。
- 選擇渲染的頻率,默認情況下面每一個event-loop(事件循環(huán))只會渲染一行(可以同pageSize自定義屬性設置)。這樣可以把大的工作量進行分隔,提供整體渲染的性能。
實例代碼
最簡單的ListView
- 運行效果如下:
代碼:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
PixelRatio,
TouchableOpacity
} from 'react-native';
// 引入本地的數(shù)據(jù)
const wineArr = require('./Wine.json');
export default class ListViewDemo extends Component {
// 構造
constructor(props) {
super(props);
// 1.創(chuàng)建數(shù)據(jù)源
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
// 初始狀態(tài)
this.state = {
dataSource: ds.cloneWithRows([''])
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
/>
);
}
componentDidMount() {
this.setState({
dataSource:this.state.dataSource.cloneWithRows(wineArr)
})
}
_renderRow(rowData, sectionID, rowID){
return(
<TouchableOpacity
style={styles.cellViewStyle}
onPress={()=>alert('點擊了第' + sectionID + '組中的第' + rowID + '行')}
>
{/*左邊*/}
<Image source={{uri: rowData.image}} style={styles.cellImgStyle}/>
{/*右邊*/}
<View style={styles.rightViewStyle}>
<Text
style={styles.mainTitleStyle}
numberOfLines={2}
>
{rowData.name}
</Text>
<Text style={styles.subTitleStyle}>¥{rowData.money}.00</Text>
</View>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
cellViewStyle:{
borderBottomWidth: 1 / PixelRatio.get(),
borderBottomColor:'#ccc',
/*主軸的方向*/
flexDirection:'row',
padding:10
},
cellImgStyle:{
width: 90,
height: 60,
resizeMode:'contain'
},
rightViewStyle:{
flex:1,
// backgroundColor:'red',
justifyContent:'space-between'
},
mainTitleStyle:{
fontSize: 15,
color:'red'
},
subTitleStyle:{
color:'#999'
}
});
AppRegistry.registerComponent('ListViewDemo', () => ListViewDemo);
ListView的吸頂效果:
點擊下載Demo
- 效果圖:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
TouchableOpacity,
} from 'react-native';
// 引入JSON數(shù)據(jù)
var CarData = require('./Car.json');
// 屏幕寬度
var Dimensions = require('Dimensions');
var {width} = Dimensions.get('window');
export default class ListSectionView extends Component {
// 構造
constructor(props) {
super(props);
//獲取組的數(shù)據(jù)
var getSectionData = (dataBlob, sectionID)=> {
return dataBlob[sectionID];
};
// 獲取行數(shù)據(jù)
var getRowData = (dataBlob, sectionID,rowID) => {
return dataBlob[sectionID + ':' + rowID];
};
// 設置初始化狀態(tài)
this.state = {
dataSource: new ListView.DataSource({
getSectionData: getSectionData,
getRowData: getRowData,
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
})
};
}
render() {
return (
<View style={styles.outViewStyle}>
<View style={styles.TopViewStyle}>
<Text style={{fontSize:30,color:'white'}}>汽車品牌</Text>
</View>
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
renderSectionHeader={this._renderSection}
/>
</View>
);
}
// 耗時操作 數(shù)據(jù)請求。
componentDidMount() {
{
this.setDataSources()
}
}
setDataSources() {
// 獲得JSON數(shù)據(jù)數(shù)組
const carArray = CarData.data;
var dataBlob = {}, sectionIDs = [], rowIDs = [], Cars = [];
//遍歷獲取section的下標和內(nèi)容
for (var i = 0; i < carArray.length; i++) {
// 將每組的title存入到dataBlob中
dataBlob[i] = carArray[i].title;
// 將組號存入sectionIDs中
sectionIDs.push(i);
//將每組中有多少行數(shù)組
rowIDs[i] = [];
// 取出每一組中的行的內(nèi)容
Cars = carArray[i].cars;
for (var j = 0; j < Cars.length; j++) {
//將行號存入數(shù)組中
rowIDs[i].push(j);
// 將每行的內(nèi)容存入到dataBlob中
dataBlob[i + ':' + j] = Cars[j];
}
}
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
});
}
//每一行數(shù)據(jù)
_renderRow(rowData) {
return (
<TouchableOpacity activeOpacity={0.5}>
<View style={styles.cellStyles}>
<Image source={{uri:rowData.icon}} style={styles.imageStyles}/>
<Text style={{fontSize:30}}>{rowData.name}</Text>
</View>
</TouchableOpacity>
)
}
//每一組數(shù)據(jù)
_renderSection(sectionData){
return (
<View style={styles.sectionHeaderStyle}>
<Text style={{fontSize:20,color:'red'}}>{sectionData}</Text>
</View>
)
}
}
const styles = StyleSheet.create({
outViewStyle:{
flex:1,
},
TopViewStyle:{
width:width,
height:80,
justifyContent:'center',
alignItems:'center',
backgroundColor:'orange'
},
sectionHeaderStyle:{
backgroundColor:'#ccc',
height:25,
// alignItems:'center',
justifyContent:'center'
},
cellStyles:{
flexDirection:'row',
borderBottomColor:'#ccc',
borderBottomWidth:1,
padding:10,
alignItems:'center'
},
imageStyles:{
width: 90,
height:90,
marginRight:10
},
});
AppRegistry.registerComponent('ListSectionView', () => ListSectionView);
-
案例技術點分析:
- 在React Native中,ScrollView組件可以使用 stickyHeaderIndices 輕松實現(xiàn) sticky 效果;而使用ListView組件時,使用 stickyHeaderIndices 則不生效。
- 如何實現(xiàn)滾動時每個section header會吸頂?
- 在
ListView
中要實現(xiàn)sticky
,需要使用cloneWithRowsAndSections
方法,將dataBlob(object)
,sectionIDs (array)
,rowIDs (array)
三個值傳進去。
dataBlob object類型
sectionIDs array婁型
rowIDs array類型
-
dataBlob
dataBlob
包含ListView
所需的所有數(shù)據(jù)(section header 和 rows),在ListView渲染數(shù)據(jù)時,使用getSectionData
和 getRowData
來渲染每一行數(shù)據(jù)。 dataBlob
的 key
值包含 sectionID + rowId
-
sectionIDs
sectionIDs 用于標識每組section。
-
rowIDs
rowIDs 用于描述每個 section 里的每行數(shù)據(jù)的位置及是否需要渲染。在ListView渲染時,會先遍歷 rowIDs 獲取到對應的 dataBlob 數(shù)據(jù)。
九宮格案例
通常情況下,我們對ListView
的操作是縱向的,如果是橫向的,則需要設置ListView
的contentContainerStyle
屬性,添加flexDirection:‘row’
讓多個ListView
在同一行顯示,而且通過flexWrap:'wrap'
進行換行。
- 運行效果:
案例代碼:
Demo下載
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
TouchableOpacity,
AlertIOS,
} from 'react-native';
const shareData = require('./shareData.json').data;
//計算
var Dimensions = require('Dimensions');
var {width} = Dimensions.get('window');
var cellWH = 100;
var clos = 3;
var VMargin = (width - clos * cellWH) / (clos + 1);
export default class ListViewShare extends Component {
// 構造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {
dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2})
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow}
contentContainerStyle={styles.contentContainerStyle}
/>
);
}
componentDidMount() {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(shareData)
})
}
_renderRow(rowData){
return(
<TouchableOpacity activeOpacity={0.5} onPress={()=>{alert(rowData.title)}}>
<View style={styles.cellStyles}>
<Image source={{uri:rowData.icon}} style={styles.imageStyles}/>
<Text>{rowData.title} </Text>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
contentContainerStyle: {
flexDirection:'row',
flexWrap:'wrap',
alignItems:'center',
},
cellStyles: {
width:cellWH,
height:cellWH,
marginLeft:VMargin,
marginTop:VMargin,
alignItems:'center',
},
imageStyles: {
width:80,
height:80,
resizeMode:'contain',
},
});
AppRegistry.registerComponent('ListViewShare', () => ListViewShare);