React Native 系列(七) -- ListView


前言

本系列是基於React Native版本號0.44.3寫的。幾乎所有的App都使用了ListView這種組件,這篇文章將學習RNListView的平鋪樣式和分組樣式。

ListView平鋪樣式

  • ListView內部是通過ListViewDataSource這個對象顯示數據的,因此使用ListView的時候需要創建一個ListViewDataSource對象。

  • ListViewDataSource構造方法創建對象的時候可以選擇性出入4個參數,描述怎么提取cell,怎么刷新cell

  • 這些參數都是函數,當產生對應事件的時候,會自動調用對應函數

    構造函數可以接收以下四種參數(都是函數):
    
    getRowData(dataBlob, sectionID, rowID); // 怎么獲取行數據            
    getSectionHeaderData(dataBlob, sectionID); // 獲取沒組頭數據
    rowHasChanged(prevRowData, nextRowData); // 決定什么情況行數據才發生改變,當行數據發生改變,就會繪制下一行
    sectionHeaderHasChanged(prevSectionData, nextSectionData); // 決定什么情況頭部數據才會發生改變,當頭部數據發生改變,就會繪制下一個組
    
  • ListViewDataSourceListView組件提供高性能的數據處理和訪問。我們需要調用clone方法從原始輸入數據中抽取數據來創建ListViewDataSource對象。

  • 要更新datasource中的數據,請(每次都重新)調用cloneWithRows方法(如果用到了section,則對應cloneWithRowsAndSections方法)clone方法會自動提取新數據並進行逐行對比(使用rowHasChanged方法中的策略),這樣ListView就知道哪些行需要重新渲染了。

平鋪樣式使用步驟

  1. 創建數據源

    • 因為改變數據的時候需要刷新界面,因此可以利用setState
    • 獲取ListViewDataSource使用ListView.DataSource
    • ListViewDataSource構造方法:決定ListView怎么去處理數據,需要傳入一個對象,這個對象有四個可選屬性,都是方法。
    • 初始化ListViewDataSource的時候,如果不需要修改提取數據的方式,只需要實現rowHasChanged,告訴什么時候刷新下一行
    • 默認ListViewDataSource有提取數據方式,可以使用默認提取方式。
    // 構造
    constructor(props) {
        super(props);
        // 初始狀態
        var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    }
    
  2. 給數據源設置數據

    // 構造
    constructor(props) {
        super(props);
        // 初始狀態
        var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
        this.state = {
            dataSource: ds.cloneWithRows(['row1', 'row2'])
        }
    }
    
  3. 實現數據源方法,確定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>
    );
}

效果圖:
圖1

ListView尾部視圖

_renderFooter(){
    return (
        <View style={[{height: 30}, {backgroundColor: 'red'}, {justifyContent:'center'}]}>
            <Text style={{textAlign: `center`}}>尾部視圖</Text>
        </View>
    )
}

效果圖:
圖2

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>
    );
}

注意:需要導入TouchableOpacityAlertIOS

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分組樣式步驟

  1. 創建數據源

    var dataSource = new ListView.DataSource({
        rowHasChanged:(r1,r2)=>r1 !== r2,
        sectionHeaderHasChanged:(s1,s2)=>s1 !== s2
    });
    
  2. 設置數據

    • 不分組使用: cloneWithRows()
    • 分組使用: cloneWithRowsAndSections()
    this.state = {
        dataSource: ds.cloneWithRowsAndSections(Data)
    }
    
  3. 渲染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,
    },
});

致謝

如果發現有錯誤的地方,歡迎各位指出,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM