React Native - ListView

點擊查看文檔

ListView常用的屬性

  • ScrollView 相關屬性樣式全部繼承

  • dataSource ListViewDataSource 設置ListView的數據源

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

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

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

  • onEndReached 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
    該方法可以返回一個可以滾動的組件。默認該會返回一個ScrollView

  • renderSectionHeader 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

Demo下載

  • 運行效果如下:
ListView.gif
代碼:
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
  • 效果圖:
吸頂效果.gif
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渲染數據時,使用getSectionDatagetRowData 來渲染每一行數據。 dataBlobkey 值包含 sectionID + rowId

dataBlob.jpg
  • sectionIDs

sectionIDs 用于標識每組section。

sectionIDs.jpg
  • rowIDs

rowIDs 用于描述每個 section 里的每行數據的位置及是否需要渲染。在ListView渲染時,會先遍歷 rowIDs 獲取到對應的 dataBlob 數據。

rowIDs.jpg
九宮格案例

通常情況下,我們對ListView的操作是縱向的,如果是橫向的,則需要設置ListViewcontentContainerStyle屬性,添加flexDirection:‘row’讓多個ListView在同一行顯示,而且通過flexWrap:'wrap'進行換行。

  • 運行效果:
九宮格.gif

案例代碼:

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 第一種情況 需求:在不同的section里面展示從同一個數據源獲取的不同內容。獲取dataSource后,將其轉換...
    大柚子08閱讀 522評論 0 1
  • 最近項目在升級RN版本,我們準備優化一下ListView,在優化過程中,發現了rowHasChanged一個大坑,...
    hushicai閱讀 826評論 0 2
  • 我慌了 一點一點的 行進在這重要的時光 我試圖用我的怯懦打動你 帶著微笑 和黑夜一起哭泣 于是我常常半夜醒來 開始...
    留子堯閱讀 160評論 2 6
  • 1,船停在碼頭是最安全的,但那不是造船的目的。 2,人呆在家里是最舒服的,但那不是人生的追求。 3,人生就像十字繡...
    漂浮的流云閱讀 442評論 0 2
  • 這是一類很棒的音樂,正文開始前,我用一首歌告訴你爵士嘻哈的美。 Luv(sic) Pt4 或許你的品位算是獨到,對...
    梵高先生和LadyBlue閱讀 4,029評論 1 7