React Native經常使用組件之ListView

1. ListView經常使用屬性

  • ScrollView 相關屬性樣式所有繼承react

  • dataSource ListViewDataSource 設置ListView的數據源git

  • initialListSize number
    設置ListView組件剛剛加載的時候渲染的列表行數,用這個屬性肯定首屏或者首頁加載的數量,而不是花大量的時間渲染加載不少頁面數據,提升性能。github

  • onChangeVisibleRows function (visibleRows,changedRows)=>void。
    當可見的行發生變化的時候回調該方法。json

  • onEndReachedThreshold number
    當偏移量達到設置的臨界值調用onEndReachedreact-native

  • onEndReached function數組

    當全部的數據項行被渲染以後,而且列表往下進行滾動。一直滾動到距離底部onEndReachedThredshold設置的值進行回調該方法。原生的滾動事件進行傳遞(經過參數的形式)。性能優化

  • pageSize number 每一次事件的循環渲染的行數數據結構

  • removeClippedSubviews booloop

    該屬性用於提供大數據列表的滾動性能。該使用的時候須要給每一行(row)的佈局添加over:'hidden'樣式。該屬性默認是開啓狀態。佈局

  • renderFooter function 方法 ()=>renderable

    在每次渲染過程當中頭和尾總會從新進行渲染。若是發現該從新繪製的性能開銷比較大的時候,可使用StaticContainer容器或者其餘合適的組件。

  • renderHeader function 方法

    在每一次渲染過程當中Footer(尾)該會一直在列表的底部,header(頭)該會一直在列表的頭部,用法同上。

  • renderRow function (rowData,sectionID,rowID,highlightRow)=>renderable
    該方法有四個參數,其中分別爲數據源中一條數據,分組的ID,行的ID,以及標記是不是高亮選中的狀態信息。

  • renderScrollComponent function

    方法 (props)=>renderable 該方法能夠返回一個能夠滾動的組件。默認該會返回一個ScrollView

  • renderSectionHeader function (sectionData,sectionID)=>renderable

    若是設置了該方法,這樣會爲每個section渲染一個粘性的header視圖。該視圖粘性的效果是當剛剛被渲染開始的時候,該會處於對應的內容的頂部,而後開始滑動的時候,該會跑到屏幕的頂端。直到滑動到下一個section的header(頭)視圖,而後被替代爲止。

  • renderSeparator function

    (sectionID,rowID,adjacentRowHighlighted)=>renderable
    若是設置該方法,會在被每一行的下面渲染一個組件做爲分隔。除了每個section分組的頭部視圖前面的最後一行。

  • scrollRenderAheadDistance number

    進行設置當該行進入屏幕多少像素之內以後就開始渲染該行

2. ListView的高階特性

  • ListView一樣支持一些高級特性,包括設置每一組的粘性的頭部(相似於iPhone)、支持設置列表的header以及footer視圖、當數據列表滑動到最底部的時候支持onEndReached方法回調、設備屏幕列表可見的視圖數據發生變化的時候回調onChangeVisibleRows以及一些性能方面的優化特性。

  • ListView設計的時候,當須要動態加載很是大的數據的時候,下面有一些方法性能優化的方法可讓咱們的ListView滾動的時候更加平滑:
    • 只更新渲染數據變化的那一行 ,rowHasChanged方法會告訴ListView組件是否須要從新渲染當前那一行。
    • 選擇渲染的頻率,默認狀況下面每個event-loop(事件循環)只會渲染一行(能夠同pageSize自定義屬性設置)。這樣能夠把大的工做量進行分隔,提供總體渲染的性能。

3. Demo1 - ListView的簡單使用

3.1 代碼

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component} from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Image,
  ListView,
  TouchableOpacity,
  AlertIOS,
} from 'react-native';

var Dimensions = require('Dimensions');
var {width, height} = Dimensions.get('window');
// 引入數據文件
var models = require("./Wine.json");

var rn0910ListViewTest01 = React.createClass({

  /**
   * 生命週期,不可更改的屬性在這裏
   * @returns {{}}
   */
  getDefaultProps() {
    return {}
  },
  /**
   * 生命週期,狀態機在這裏
   * @returns {{}}
   */
  getInitialState() {
    // 建立數據源   rowHasChanged方法決定了ListView是否從新渲染當前這一行
    var ds = new ListView.DataSource({
      rowHasChanged: (r1, r2) => {
        r1 !== r2
      }
    });
    return {
      // 數據源中的數據
      dataSource: ds.cloneWithRows(models)
    }
  },
  /**
   * 生命週期,複雜的操做在這裏
   */
  componentDidMount() {

  },
  /**
   * 生命週期,渲染
   * @returns {XML}
   */
  render() {
    return (
      <ListView
        dataSource={this.state.dataSource} // 指定數據源
        renderRow={this.renderRow} // 渲染每一行
      />
    );
  },
  /**
   * ListView根據數據源的數據進行渲染
   * @param rowData 每一項的數據
   * @param sectionID 組號
   * @param rowID 行號
   * @param highlightRow
   * @returns {XML}
   */
  renderRow(rowData, sectionID, rowID, highlightRow) {
    return (
      <TouchableOpacity 
        activeOpacity={0.7} 
        onPress={() => this.cellDidClick(rowID, rowData)}
      >
        <View style={styles.wineCell}>
          <Image style={styles.icon} source={{uri: rowData.image}}/>
          <View style={styles.titleContainer}>
            <Text style={styles.title}>{rowData.name}</Text>
            <Text style={styles.subTitle}>${rowData.money}</Text>
          </View>
        </View>
      </TouchableOpacity>
    );
  },

  cellDidClick(rowID, rowData) {
    alert("點擊了" + rowID + rowData.name);
  }
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    paddingTop: 20
  },
  wineCell: {
    flexDirection: "row",
    borderBottomWidth: 1,
    borderBottomColor: '#eee',
    paddingLeft: 10,
    paddingTop: 10,
    paddingBottom: 10,
    backgroundColor: 'white'
  },
  icon: {
    width: 60,
    height: 60,
  },
  titleContainer: {
    flexDirection: "column",
    justifyContent: 'space-between'
  },
  title: {
    fontSize: 15,
    fontWeight: 'bold',
    width: width - 60,
    paddingLeft: 10,
    paddingRight: 10
  },
  subTitle: {
    fontSize: 15,
    marginLeft: 10,
  }
});

AppRegistry.registerComponent('rn0910ListViewTest01', () => rn0910ListViewTest01);

3.2 效果圖

4. ListView之九宮格佈局

listViewContentStyle: {
    // ListView實現九宮格佈局,主要是這倆屬性
    flexDirection: 'row',
    flexWrap: 'wrap'
  },
  cellStyle: {
    alignItems: 'center',
    // 每一項設置寬高
    width: cellWH,
    height: cellWH,
    marginLeft: hMargin,
    marginTop: tMargin
  },

4.1 代碼

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component} from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ListView,
  Image,
  TouchableOpacity,
  AlertIOS
} from 'react-native';

// 引入json文件
var models = require("./shareData.json");
// 獲取屏幕寬度
var Dimensions = require('Dimensions');
var screen_Width = Dimensions.get('window').width;

var col = 3; // 列數
var cellWH = 100; // 每項寬高
var tMargin = 25; // 行 頂間距
var hMargin = (screen_Width - cellWH * col) / (col + 1); // 每項之間的間距

var rn0912ListViewTest02 = React.createClass({
  /**
   * 不可修改的屬性
   * @returns {{}}
   */
  getDefaultProps() {
    return {}
  },
  /**
   * 狀態機
   */
  getInitialState() {
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    return {
      dataSource: ds.cloneWithRows(models.data)
    }
  },
  /**
   * 渲染
   * @returns {{}}
   */
  render() {
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        contentContainerStyle={styles.listViewContentStyle}
      />
    )
  },
  /**
   * 操做
   */
  componentDidMount() {

  },
  renderRow(rowData) {
    return (
      <TouchableOpacity
        activeOpacity={0.8}
        onPress={() => {
          alert("點擊分享到" + rowData.title)
        }}
      >
        <View style={styles.cellStyle}>
          <Image style={styles.iconStyle} source={{uri: rowData.icon}}/>
          <Text style={styles.nameStyle}>{rowData.title}</Text>
        </View>
      </TouchableOpacity>
    );
  }
});

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  listViewContentStyle: {
    // ListView實現九宮格佈局,主要是這倆屬性
    flexDirection: 'row',
    flexWrap: 'wrap'
  },
  cellStyle: {
    alignItems: 'center',
    // 每一項設置寬高
    width: cellWH,
    height: cellWH,
    marginLeft: hMargin,
    marginTop: tMargin
  },
  iconStyle: {
    width: 80,
    height: 80,
  },
  nameStyle: {
    fontSize: 14
  }
});

AppRegistry.registerComponent('rn0912ListViewTest02', () => rn0912ListViewTest02);

4.2 效果圖

5. ListView黏性sectionHeader

5.1 技術點分析

  • 如何實現滾動時每一個sectionHeader會吸頂?

    在ListView中要實現sticky,須要使用cloneWithRowsAndSections方法,將dataBlob(object),sectionIDs(array),rowIDs(array)三個值傳進去.

dataBlob

dataBlob包含ListView所需的全部的數據(sectionHeader 和 rows),在ListView渲染數據時,使用getSectionData 和 getRowData 來渲染每一行數據. dataBlob的 key 值包含sectionID + rowID

sectionIDs

sectionIDs 用語表示每組section

rowIDs 二維數組

rowIDs 用於描述每一個section裏的每行數據的位置及是否須要渲染. 在ListView渲染時,會先遍歷 rowIDs 獲取到對應的 dataBlob 數據

模擬對應的數據結構

在 DataSource 中, 設置ListView獲取 row 和 section 的方法

getInitialState() {

    var getSectionHeaderData = (dataBlob, sectionID) => {
      return dataBlob[sectionID];
    }
    var getRowData = (dataBlob, sectionID, rowID) => {
      return dataBlob[sectionID + ":" + rowID];
    }

    return {
      // 設置數據源,數據龐大,在
      dataSource: new ListView.DataSource({
        getSectionHeaderData: getSectionHeaderData, // 獲取組數據
        getRowData: getRowData, // 獲取行數據
        rowHasChanged: (r1, r2) => {
          r1 !== r2
        }, // 何時建立行
        sectionHeaderHasChanged: (s1, s2) => {
          s1 !== s2
        } // 何時建立組
      })
    };
  },

刷新狀態機,將數據傳入到ListView中

this.setState({
 dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
});

ListView中不只要renderRow 還要 renderSectionHeader

<ListView
     dataSource={this.state.dataSource}
     renderRow={this.renderRow}
     renderSectionHeader={this.renderSectionHeader}
   />

5.2 代碼

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, {Component} from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ListView,
  Image,
  TouchableOpacity
} from 'react-native';

var carData = require("./Car.json");

var rn0913ListViewTest03 = React.createClass({
  /**
   * 不可更改的屬性
   * @returns {{}}
   */
  getDefaultProps() {
    return {};
  },
  /**
   * 狀態機
   * @returns {{}}
   */
  getInitialState() {

    var getSectionHeaderData = (dataBlob, sectionID) => {
      return dataBlob[sectionID];
    }
    var getRowData = (dataBlob, sectionID, rowID) => {
      return dataBlob[sectionID + ":" + rowID];
    }

    return {
      // 這裏就沒有指定數據源,因爲須要處理數據,因此在componentDidMount()方法中進行數據處理
      dataSource: new ListView.DataSource({
        getSectionHeaderData: getSectionHeaderData, // 獲取組數據
        getRowData: getRowData, // 獲取行數據
        rowHasChanged: (r1, r2) => {
          r1 !== r2
        }, // 何時建立行
        sectionHeaderHasChanged: (s1, s2) => {
          s1 !== s2
        } // 何時建立組
      })
    };
  },
  /**
   * 渲染界面
   */
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.nav}>
          <Text style={styles.navTitle}>Chaos車庫</Text>
        </View>
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this.renderRow}
          renderSectionHeader={this.renderSectionHeader}
        />
      </View>
    );
  },

  /**
   * 渲染每一項
   * @param rowData
   * @returns {XML}
   */
  renderRow(rowData) {
    return (
      <TouchableOpacity activeOpacity={0.8}>
        <View style={styles.cellStyle}>
          <Image style={styles.imgStyle} source={{uri: rowData.icon}}/>
          <Text style={styles.carTitleStyle}>{rowData.name}</Text>
        </View>
      </TouchableOpacity>
    );
  },
  /**
   * 渲染每一組
   * @param sectionData
   * @returns {XML}
   */
  renderSectionHeader(sectionData) {
    return (
      <TouchableOpacity activeOpacity={0.8}>
        <View style={styles.headerStyle}>
          <Text style={styles.headerTextStyle}>{sectionData}</Text>
        </View>
      </TouchableOpacity>
    );
  },

  /**
   * 複雜操做
   */
  componentDidMount() {
    this.loadDataFromJson();
  },
  /**
   * 加載數據,處理數據
   */
  loadDataFromJson() {
    var jsonData = carData.data;
    var dataBlob = {}, // 對象
      sectionIDs = [],
      rowIDs = [], // 這是個二維數組
      cars = [];

    for (var i = 0; i < jsonData.length; i++) {
      // 肯定每組的id
      sectionIDs.push(i);
      // 肯定每組的id對應的組數據
      dataBlob[i] = jsonData[i].title;
      // 獲取行集合
      cars = jsonData[i].cars;
      rowIDs[i] = []; // 二維數組,每一項都是行id的數組
      for (var j = 0; j < cars.length; j++) {
        rowIDs[i].push(j);
        // 肯定每一行的數據
        dataBlob[i + ":" + j] = cars[j];
      }
    }
    // 給ListView的數據源賦值
    this.setState({
      dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
    });
  }
});


const styles = StyleSheet.create({
  container: {},
  nav: {
    height: 64,
    backgroundColor: '#eee',
    alignItems: 'center',
    justifyContent: 'center'
  },
  navTitle: {
    fontSize:26
  },
  headerStyle: {
    backgroundColor: '#ccc',
    height:30,
  },
  headerTextStyle: {
    lineHeight:30,
    paddingLeft:10
  },
  cellStyle: {
    borderBottomColor:'#eee',
    borderBottomWidth:0.5,
    flexDirection:'row',
    alignItems:'center',
    padding:10
  },
  imgStyle: {
    width: 60,
    height: 60
  },
  carTitleStyle: {
    marginLeft:10
  }
});

AppRegistry.registerComponent('rn0913ListViewTest03', () => rn0913ListViewTest03);

5.3 效果圖

感謝做者,原文地址,戳我

相關文章
相關標籤/搜索