點擊查看文檔
ListView常用的屬性
ScrollView 相關屬性樣式全部繼承
dataSource ListViewDataSource 設置ListView的數據源
initialListSize number
設置ListView組件剛剛加載的時候渲染的列表行數,用這個屬性確定首屏或者首頁加載的數量,而不是花大量的時間渲染加載很多頁面數據,提高性能。onChangeVisibleRows function
(visibleRows,changedRows)=>void。
當可見的行發生變化的時候回調該方法。onEndReachedThreshold number
當偏移量達到設置的臨界值調用onEndReachedonEndReached function
當所有的數據項行被渲染之后,并且列表往下進行滾動。一直滾動到距離底部onEndReachedThredshold設置的值進行回調該方法。原生的滾動事件進行傳遞(通過參數的形式)。pageSize number 每一次事件的循環渲染的行數
removeClippedSubviews bool
該屬性用于提供大數據列表的滾動性能。該使用的時候需要給每一行(row)的布局添加over:'hidden'樣式。該屬性默認是開啟狀態。renderFooter function 方法 ()=>renderable
在每次渲染過程中頭和尾總會重新進行渲染。如果發現該重新繪制的性能開銷比較大的時候,可以使用StaticContainer容器或者其他合適的組件。renderHeader function 方法
在每一次渲染過程中Footer(尾)該會一直在列表的底部,header(頭)該會一直在列表的頭部,用法同上。renderRow function (rowData,sectionID,rowID,highlightRow)=>renderable
該方法有四個參數,其中分別為數據源中一條數據,分組的ID,行的ID,以及標記是否是高亮選中的狀態信息。renderScrollComponent function 方法 (props)=>renderable
該方法可以返回一個可以滾動的組件。默認該會返回一個ScrollViewrenderSectionHeader function (sectionData,sectionID)=>renderable
如果設置了該方法,這樣會為每一個section渲染一個粘性的header視圖。該視圖粘性的效果是當剛剛被渲染開始的時候,該會處于對應的內容的頂部,然后開始滑動的時候,該會跑到屏幕的頂端。直到滑動到下一個section的header(頭)視圖,然后被替代為止。renderSeparator function (sectionID,rowID,adjacentRowHighlighted)=>renderable
如果設置該方法,會在被每一行的下面渲染一個組件作為分隔。除了每一個section分組的頭部視圖前面的最后一行。scrollRenderAheadDistance number
進行設置當該行進入屏幕多少像素以內之后就開始渲染該行
ListView的高階特性
ListView同樣支持一些高級特性,包括設置每一組的粘性的頭部(類似于iPhone)、支持設置列表的header以及footer視圖、當數據列表滑動到最底部的時候支持onEndReached方法回調、設備屏幕列表可見的視圖數據發生變化的時候回調onChangeVisibleRows以及一些性能方面的優化特性。
-
ListView設計的時候,當需要動態加載非常大的數據的時候,下面有一些方法性能優化的方法可以讓我們的ListView滾動的時候更加平滑:
- 只更新渲染數據變化的那一行 ,rowHasChanged方法會告訴ListView組件是否需要重新渲染當前那一行。
- 選擇渲染的頻率,默認情況下面每一個event-loop(事件循環)只會渲染一行(可以同pageSize自定義屬性設置)。這樣可以把大的工作量進行分隔,提供整體渲染的性能。
實例代碼
最簡單的ListView
- 運行效果如下:
代碼:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image,
PixelRatio,
TouchableOpacity
} from 'react-native';
// 引入本地的數據
const wineArr = require('./Wine.json');
export default class ListViewDemo extends Component {
// 構造
constructor(props) {
super(props);
// 1.創建數據源
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
// 初始狀態
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數據
var CarData = require('./Car.json');
// 屏幕寬度
var Dimensions = require('Dimensions');
var {width} = Dimensions.get('window');
export default class ListSectionView extends Component {
// 構造
constructor(props) {
super(props);
//獲取組的數據
var getSectionData = (dataBlob, sectionID)=> {
return dataBlob[sectionID];
};
// 獲取行數據
var getRowData = (dataBlob, sectionID,rowID) => {
return dataBlob[sectionID + ':' + rowID];
};
// 設置初始化狀態
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>
);
}
// 耗時操作 數據請求。
componentDidMount() {
{
this.setDataSources()
}
}
setDataSources() {
// 獲得JSON數據數組
const carArray = CarData.data;
var dataBlob = {}, sectionIDs = [], rowIDs = [], Cars = [];
//遍歷獲取section的下標和內容
for (var i = 0; i < carArray.length; i++) {
// 將每組的title存入到dataBlob中
dataBlob[i] = carArray[i].title;
// 將組號存入sectionIDs中
sectionIDs.push(i);
//將每組中有多少行數組
rowIDs[i] = [];
// 取出每一組中的行的內容
Cars = carArray[i].cars;
for (var j = 0; j < Cars.length; j++) {
//將行號存入數組中
rowIDs[i].push(j);
// 將每行的內容存入到dataBlob中
dataBlob[i + ':' + j] = Cars[j];
}
}
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
});
}
//每一行數據
_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>
)
}
//每一組數據
_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 輕松實現 sticky 效果;而使用ListView組件時,使用 stickyHeaderIndices 則不生效。
- 如何實現滾動時每個section header會吸頂?
- 在
ListView
中要實現sticky
,需要使用cloneWithRowsAndSections
方法,將dataBlob(object)
,sectionIDs (array)
,rowIDs (array)
三個值傳進去。
dataBlob object類型
sectionIDs array婁型
rowIDs array類型
-
dataBlob
dataBlob
包含ListView
所需的所有數據(section header 和 rows),在ListView渲染數據時,使用getSectionData
和 getRowData
來渲染每一行數據。 dataBlob
的 key
值包含 sectionID + rowId
-
sectionIDs
sectionIDs 用于標識每組section。
-
rowIDs
rowIDs 用于描述每個 section 里的每行數據的位置及是否需要渲染。在ListView渲染時,會先遍歷 rowIDs 獲取到對應的 dataBlob 數據。
九宮格案例
通常情況下,我們對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);
// 初始狀態
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);