前言
本系列是基於React Native
版本號0.44.3
寫的。幾乎所有的App都使用了ListView
這種組件,這篇文章將學習RN
中ListView
的平鋪樣式和分組樣式。
ListView平鋪樣式
-
ListView
內部是通過ListViewDataSource
這個對象顯示數據的,因此使用ListView
的時候需要創建一個ListViewDataSource
對象。 -
ListViewDataSource
構造方法創建對象的時候可以選擇性出入4個參數,描述怎么提取cell,怎么刷新cell -
這些參數都是函數,當產生對應事件的時候,會自動調用對應函數
構造函數可以接收以下四種參數(都是函數): getRowData(dataBlob, sectionID, rowID); // 怎么獲取行數據 getSectionHeaderData(dataBlob, sectionID); // 獲取沒組頭數據 rowHasChanged(prevRowData, nextRowData); // 決定什么情況行數據才發生改變,當行數據發生改變,就會繪制下一行 sectionHeaderHasChanged(prevSectionData, nextSectionData); // 決定什么情況頭部數據才會發生改變,當頭部數據發生改變,就會繪制下一個組
-
ListViewDataSource
為ListView
組件提供高性能的數據處理和訪問。我們需要調用clone
方法從原始輸入數據中抽取數據來創建ListViewDataSource
對象。 -
要更新
datasource
中的數據,請(每次都重新)調用cloneWithRows
方法(如果用到了section
,則對應cloneWithRowsAndSections
方法)clone
方法會自動提取新數據並進行逐行對比(使用rowHasChanged
方法中的策略),這樣ListView
就知道哪些行需要重新渲染了。
平鋪樣式使用步驟
-
創建數據源
- 因為改變數據的時候需要刷新界面,因此可以利用
setState
- 獲取
ListViewDataSource
使用ListView.DataSource
ListViewDataSource
構造方法:決定ListView
怎么去處理數據,需要傳入一個對象,這個對象有四個可選屬性,都是方法。- 初始化
ListViewDataSource
的時候,如果不需要修改提取數據的方式,只需要實現rowHasChanged
,告訴什么時候刷新下一行 - 默認
ListViewDataSource
有提取數據方式,可以使用默認提取方式。
// 構造 constructor(props) { super(props); // 初始狀態 var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); }
- 因為改變數據的時候需要刷新界面,因此可以利用
-
給數據源設置數據
// 構造 constructor(props) { super(props); // 初始狀態 var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); this.state = { dataSource: ds.cloneWithRows(['row1', 'row2']) } }
-
實現數據源方法,確定cell
- 這個方法會自動傳入四個參數(rowData, sectionID, rowID, highlightRow)
- rowData: 行數據
- sectionID: 當前行所在的組id
- rowID: 當前行所在的行id
- highlightRow: 高亮函數
render() { return ( <ListView style={{marginTop: 20}} dataSource={this.state.dataSource} renderRow={this._renderRow.bind(this)} /> ); } _renderRow(rowData, sectionID, rowID, highlightRow){ return ( <View> <Text>{rowData}</Text> </View> ); }
ListView分割線
運行上面的代碼,你會發現ListView
沒有分割線,我們可以添加分割線:
render() {
return (
<ListView style={{marginTop: 20}}
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
renderSeparator={this._renderSeparator.bind(this)}
/>
);
}
_renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
return (
<View style={{height:1,backgroundColor:'black'}}>
</View>
)
}
ListView頭部視圖
_renderHeader(){
return (
<View style={[{height:30}, {backgroundColor:'red'},{justifyContent: 'center'}]}>
<Text style={[{textAlign: 'center'}]}>頭部視圖</Text>
</View>
);
}
效果圖:
ListView尾部視圖
_renderFooter(){
return (
<View style={[{height: 30}, {backgroundColor: 'red'}, {justifyContent:'center'}]}>
<Text style={{textAlign: `center`}}>尾部視圖</Text>
</View>
)
}
效果圖:
ListView點擊cell高亮
_renderRow(rowData, sectionID, rowID, highlightRow){
return (
<TouchableOpacity onPress={()=>{
AlertIOS.alert(rowID)
highlightRow(sectionID, rowID)
}}>
<View style={{height: 40}}>
<Text>{rowData}</Text>
</View>
</TouchableOpacity>
);
}
注意:需要導入TouchableOpacity
和AlertIOS
ListView分組樣式
有時候我們會遇到ListView
分組樣式,比如中國有多少個省,然后每個省又有多少個城市。
要想明白ListView
是如何分組的,就需要知道ListView
底層是如何獲取組數據,行數據。
ListView分組原理
ListView
默認支持3種格式的數據,只要按照這3種格式處理數據,就會自動獲取數據,從而達到分組樣式
默認的3種格式的數據:
// 格式一
[[row_0, row_1,...],[row_0, row_1,...],...]
// 格式二
{sectionID_0:{rowID_0, rowID_1, rowID_2, ...}, ...}
// 格式三
{sectionID_0:[rowID_0, rowID_1, ...], ...}
實現ListView分組樣式步驟
-
創建數據源
var dataSource = new ListView.DataSource({ rowHasChanged:(r1,r2)=>r1 !== r2, sectionHeaderHasChanged:(s1,s2)=>s1 !== s2 });
-
設置數據
- 不分組使用:
cloneWithRows()
- 分組使用:
cloneWithRowsAndSections()
this.state = { dataSource: ds.cloneWithRowsAndSections(Data) }
- 不分組使用:
-
渲染
ListView
代碼演練
這個例子我們使用了本地假數據,創建一個Data.json
文件,它看起來是這樣:
[
["section0-row0","section0-row1","section0-row2","section0-row3"],
["section1-row0","section1-row1","section1-row2","section1-row3"],
["section2-row0","section2-row1","section2-row2","section2-row3"],
["section3-row0","section3-row1","section3-row2","section3-row3"],
["section4-row0","section4-row1","section4-row2","section4-row3"],
["section5-row0","section5-row1","section5-row2","section5-row3"]
]
我們在index.ios.js
里面引用Data.json
var Data = require('./Data.json')
然后就按照上述 實現ListView
分組樣式 步驟寫:
var Data = require('./Data.json')
// 主組件
export default class RNDemoOne extends Component {
// 構造
constructor(props) {
super(props);
// 初始狀態
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2
});
this.state = {
dataSource: ds.cloneWithRowsAndSections(Data)
}
}
render() {
return (
<ListView style={{marginTop: 20}}
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
renderSeparator={this._renderSeparator.bind(this)} renderSectionHeader={this._renderSectionHeader.bind(this)}
/>
);
}
_renderSectionHeader(sectionData, sectionID){
return (
<View style={[{height: 40}, {backgroundColor:'red'}]}>
<View style={[{flex:1}, {justifyContent: 'center'}]}>
<Text style={{paddingLeft: 10}}>{sectionID}</Text>
</View>
</View>
)
}
_renderRow(rowData, sectionID, rowID, highlightRow){
return (
<TouchableOpacity onPress={()=>{
AlertIOS.alert(rowID)
highlightRow(sectionID, rowID)
}}>
<View style={{height: 40}}>
<Text>{rowData}</Text>
</View>
</TouchableOpacity>
);
}
_renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
return (
<View style={{height:1,backgroundColor:'black'}}>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
致謝
如果發現有錯誤的地方,歡迎各位指出,謝謝!