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

    致谢

    如果发现有错误的地方,欢迎各位指出,谢谢!

  • 相关阅读:
    [转]relative、absolute和float
    CSS 布局属性(display,float,clear,visibility,overflow,overflow-x,overflow-y)
    CSS 块状元素和内联元素
    CSS定位
    CSS实例
    jQuery简单实例
    [转]几种常见SQL分页方式
    MyBatis Mapper XML 文件
    MyBatis XML 映射配置文件
    MyBatis 入门
  • 原文地址:https://www.cnblogs.com/scott-mr/p/7346613.html
Copyright © 2020-2023  润新知