• React Native 列表的总结


    React Native 列表的总结

    FlatListSectionList都是React Native中高性能的列表组件。这些新的列表组件在性能方面都有了极大的提升, 其中最主要的一个是无论列表有多少行,它的内存使用都是常数级的。他们有着共同的特点:

    • 完全跨平台。
    • 行组件显示或隐藏时可配置回调事件。
    • 支持单独的头部组件。
    • 支持单独的尾部组件。
    • 支持自定义行间分隔线。
    • 支持下拉刷新。
    • 支持上拉加载。

    实质两者都是基于VirtualizedList组件的封装,因此需要注意:

    • 当某行滑出渲染区域之外后,其内部状态将不会保留。请确保你在行组件以外的地方保留了数据。
    • 为了优化内存占用同时保持滑动的流畅,列表内容会在屏幕外异步绘制。这意味着如果用户滑动的速度超过渲染的速度,则会先看到空白的内容。这是为了优化不得不作出的妥协,而官方也在设法持续改进。
    • 本组件继承自PureComponent而非通常的Component,这意味着如果其props在浅比较中是相等的,则不会重新渲染。所以请先检查你的renderItem函数所依赖的props数据(包括data属性以及可能用到的父组件的state),如果是一个引用类型(Object或者数组都是引用类型),则需要先修改其引用地址(比如先复制到一个新的Object或者数组中),然后再修改其值,否则界面很可能不会刷新。
    • 默认情况下每行都需要提供一个不重复的key属性。你也可以提供一个keyExtractor函数来生成key。

    当然,他们也有着一些不同的特性,下面来主要说明一下。

    FlatList

    FlatList是一个高性能的简单列表组件,用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同。

    除了上述的特性外,FlatList还有:

    • 支持水平布局模式。
    • 支持跳转到指定行(ScrollToIndex)

    FlatList更适于长列表数据,且元素个数可以增删。和ScrollView不同的是,FlatList并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。

    FlatList组件必须的两个属性是datarenderItemdata是列表的数据源,而renderItem则从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。

    简单的例子:

    
    <FlatList
      data={[{key: 'a'}, {key: 'b'}]}
      renderItem={({item}) => <Text>{item.key}</Text>}
    />
    
    

    FlatList主要属性

    属性 说明
    data 为了简化起见,data属性目前只支持普通数组。如果需要使用其他特殊数据结构,例如immutable数组,请直接使用更底层的VirtualizedList组件。
    getItemLayout getItemLayout是一个可选的优化,用于避免动态测量内容尺寸的开销,不过前提是你可以提前知道内容的高度。如果你的行高是固定的,getItemLayout用起来就既高效又简单,类似下面这样:getItemLayout={(data, index) => ( {length: 行高, offset: 行高 * index, index} )}
    keyExtractor 此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。
    renderItem 根据行数据data渲染每一行的组件。

    FlatList主要的方法

    方法 说明 代码
    scrollTo() 滚动到指定的x, y偏移处。第三个参数为是否启用平滑滚动动画。 scrollTo(([y]: number), object, ([x]: number), ([animated]: boolean));
    scrollToEnd() 滚动到视图底部(水平方向的视图则滚动到最右边)。加上动画参数scrollToEnd({animated: true})则启用平滑滚动动画,或是调用scrollToEnd({animated: false})来立即跳转。如果不使用参数,则animated选项默认启用。 scrollToEnd(([options]: object));
    flashScrollIndicators() 短暂地显示滚动指示器。 flashScrollIndicators();

    实例:电影列表

    以下是获取豆瓣电影数据并展示成列表的实例。

    
    import React, {Component} from "react";
    
    import {ActivityIndicator, FlatList, Image, StyleSheet, Text, View} from "react-native";
    
    const REQUEST_URL =
        "http://api.douban.com/v2/movie/top250?count=50";
    
    export class SampleAppMovies extends Component {
    
        static navigationOptions = {
            title: '电影列表页      ',
            headerStyle: {
                backgroundColor: '#8bc9ff',
            }
        };
    
        constructor(props) {
            super(props);
            this.state = {
                data: [],
                loaded: false
            };
            // 在ES6中,如果在自定义的函数里使用了this关键字,则需要对其进行“绑定”操作,否则this的指向会变为空
            // 像下面这行代码一样,在constructor中使用bind是其中一种做法(还有一些其他做法,如使用箭头函数等)
            this.fetchData = this.fetchData.bind(this);
        }
    
        componentDidMount() {
            this.fetchData();
        }
    
        fetchData() {
            fetch(REQUEST_URL)
                .then(response => response.json())
                .then(responseData => {
                    this.setState({
                        data: this.state.data.concat(responseData.subjects),
                        loaded: true
                    });
                });
        }
    
        static renderLoadingView() {
            return (
                <View style={styles.container}>
                    <ActivityIndicator size="large" color="#8bc9ff"/>
                </View>
            );
        }
    
        static renderMovie({item}) {
            // { item }是一种“解构”写法,请阅读ES2015语法的相关文档
            // item也是FlatList中固定的参数名,请阅读FlatList的相关文档
            return (
                <View style={styles.container}>
                    <Image
                        source={{uri: item.images.medium}}
                        style={styles.thumbnail}/>
                    <View style={styles.rightContainer}>
                        <Text style={styles.title}>{item.title}</Text>
                        <Text style={styles.year}>{item.year}</Text>
                        <Text style={styles.introduce}>{"评分:"}
                            <Text
                                style={styles.ratingNum}>{item.rating.average === 0 ? "暂无评价" : item.rating.average}
                            </Text>
                        </Text>
                        <Text numberOfLines={1} style={styles.introduce}>{"导演:"}
                            <Text style={styles.info}>{item.directors[0].name}</Text>
                        </Text>
                        <Text numberOfLines={1} style={styles.introduce}>{"演员:"}
                            <Text style={styles.info}>{item.casts[0].name + " "}</Text>
                            <Text style={styles.info}>{item.casts[1].name + " "}</Text>
                            <Text style={styles.info}>{item.casts[2].name}</Text>
                        </Text>
                    </View>
                </View>
            );
        }
    
        render() {
            if (!this.state.loaded) {
                return SampleAppMovies.renderLoadingView();
            }
    
            return (
                <FlatList
                    data={this.state.data}
                    ItemSeparatorComponent={ItemDivideComponent}
                    renderItem={SampleAppMovies.renderMovie}
                    style={styles.list}
                    keyExtractor={(item, index) => item.id}
                />
            );
        }
    }
    
    class ItemDivideComponent extends Component {
        render() {
            return (
                <View style={{height: 0.5, backgroundColor: 'gray'}}/>
            );
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            flexDirection: "row",
            justifyContent: "center",
            backgroundColor: "#F5FCFF",
            padding: 5
        },
        rightContainer: {
            flex: 1,
            marginLeft: 10,
            flexDirection: 'column',
        },
        title: {
            color: '#000',
            fontWeight: 'bold',
            fontSize: 20,
            marginBottom: 9,
            justifyContent: 'flex-start',
        },
        year: {
            fontSize: 15,
            marginBottom: 5,
        },
        introduce: {
            flex: 1,
            fontSize: 14,
            color: '#000',
            marginBottom: 5,
        },
        ratingNum: {
            flex: 1,
            fontSize: 20,
            fontWeight: 'bold',
            color: '#ffad24'
        },
        info: {
            fontSize: 16,
            color: '#000',
            marginRight: 3,
            marginBottom: 5,
        },
        thumbnail: {
             90,
            height: 145
        },
        list: {
            backgroundColor: "#FFF"
        }
    });
    
    

    电影列表

    SectionList

    如果要渲染的是一组需要分组的数据,也许还带有分组标签的,那么SectionList将是个不错的选择。

    SectionList高性能的分组(section)列表组件。除了最一开始说明的共性外,它还有:

    • 支持分组的头部组件。
    • 支持分组的分隔线。
    • 支持多种数据源结构。

    简单的例子:

    
    <SectionList
      renderItem={({item}) => <ListItem title={item.title} />}
      renderSectionHeader={({section}) => <H1 title={section.key} />}
      sections={[ // homogeneous rendering between sections
        {data: [...], key: ...},
        {data: [...], key: ...},
        {data: [...], key: ...},
      ]}
    />
    
    <SectionList
      sections={[ // heterogeneous rendering between sections
        {data: [...], key: ..., renderItem: ...},
        {data: [...], key: ..., renderItem: ...},
        {data: [...], key: ..., renderItem: ...},
      ]}
    />
    
    

    SectionList主要属性

    属性 说明
    sections 用来渲染的数据,类似于FlatList中的data属性。
    renderItem 用来渲染每一个section中的每一个列表项的默认渲染器。可以在section级别上进行覆盖重写。必须返回一个react组件。
    keyExtractor 此函数用于为给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。注意这只设置了每行(item)的key,对于每个组(section)仍然需要另外设置key。

    SectionList主要的方法

    方法 说明 代码
    scrollToLocation() 将可视区内位于特定sectionIndex 或 itemIndex (section内)位置的列表项,滚动到可视区的制定位置。 scrollToLocation(params);parms在下面说明
    recordInteraction() 主动通知列表发生了一个事件,以使列表重新计算可视区域。比如说当waitForInteractions 为 true 并且用户没有滚动列表时,就可以调用这个方法。不过一般来说,当用户点击了一个列表项,或发生了一个导航动作时,我们就可以调用这个方法。 recordInteraction();
    flashScrollIndicators() 短暂地显示滚动指示器。 flashScrollIndicators();

    Valid params keys are:

    • animated (boolean) - Whether the list should do an animation while scrolling. Defaults to true.
    • itemIndex (number) - Index within section for the item to scroll to. Required.
    • sectionIndex (number) - Index for section that contains the item to scroll to. Required.
    • viewOffset (number) - 一个以像素为单位,到最终位置偏移距离的固定值,比如为了弥补粘接的header所占据的空间。
    • viewPosition (number) - A value of 0 places the item specified by index at the top, 1 at the bottom, and 0.5 centered in the middle.

    对于scrollToLocation(params);还有需要注意的地方: 如果没有设置getItemLayout或是onScrollToIndexFailed,就不能滚动到位于外部渲染区的位置。

    SectionList注意点

    对于计算滑动到那个点,可以使用scrollToLocation

    
    this.sectionList.scrollToLocation({
      sectionIndex: 2,
      itemIndex: 2,
    viewOffset: 30,
    })
    ....
    <SectionList
        ref={ref => this.sectionList = ref}
    />
    
    

    但是如果要调用scrollToLocation的时候很可能页面还没渲染好,RN并不知道需要滚动到哪个位置,这个时候需要配合getItemLayout来使用。如果在sectionList中使用了该属性,RN会调用此方法计算列表中各项的显示位置,从而提前得知怎么处理滚动。

    
    getItemLayout={(data, index) => ({
        index,
        offset: OFFSET_FROM_TOP,
        length: ITEM_HEIGHT.
        })
      }
    
    

    不过对于SectionList计算滚动到那个点的位置是比较困难的,要计算section头部高度,也要计算item的高度,同时如果存在下画线也需要考虑在内,这就如果滑动时可能会出现偏移。

    在这里可以使用库rn-section-list-get-item-layout来帮助我们解决问题:

    
    ...
    constructor(props) {
        super(props)
    
        this.getItemLayout = sectionListGetItemLayout({
          // The height of the row with rowData at the given sectionIndex and rowIndex
          getItemHeight: (rowData, sectionIndex, rowIndex) => sectionIndex === 0 ? 100 : 50,
    
          // These four properties are optional
          getSeparatorHeight: () => 1 / PixelRatio.get(), // The height of your separators
          getSectionHeaderHeight: () => 20, // The height of your section headers
          getSectionFooterHeight: () => 10, // The height of your section footers
          listHeaderHeight: 40, // The height of your list header
        })
      }
    ...
    
    
    

    实例:城市选择列表

    
    import React, {Component} from 'react';
    import {SectionList, StyleSheet, Text, ToastAndroid, TouchableOpacity, View} from 'react-native';
    import sectionListGetItemLayout from 'react-native-section-list-get-item-layout'
    import _ from 'lodash';
    import cityData from '../json/city.json'
    
    const ITEM_HEIGHT = 45;
    
    //城市字母
    const letters = _
        .range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1)
        .map(n => String.fromCharCode(n).substr(0));
    
    _.pull(letters, 'O', 'V');
    
    //城市的数组
    let city = [];
    
    export class CitySelectList extends Component {
    
        static navigationOptions = {
            title: '列表页      ',
            headerStyle: {
                backgroundColor: '#8bc9ff',
            }
        };
    
        constructor(props) {
            super(props);
    
            this.getItemLayout = sectionListGetItemLayout({
                getItemHeight: (rowData, sectionIndex, rowIndex) => ITEM_HEIGHT,
                getSeparatorHeight: () => 0,
                getSectionHeaderHeight: () => ITEM_HEIGHT,
                getSectionFooterHeight: () => 0,
                listHeaderHeight: 0
            })
        }
    
        componentWillMount() {
            //把城市放到对应的字母中
            for (let j = 0; j < letters.length; j++) {
    
                let each = [];
    
                for (let i = 0; i < cityData.CITIES.length; i++) {
                    if (letters[j] === cityData.CITIES[i].name_en.substr(0, 1)) {
                        each.push(cityData.CITIES[i].name);
                    }
                }
    
                let _city = {};
                _city.key = letters[j];
                _city.data = each;
    
                city.push(_city)
            }
    
            //同步城市信息
            this.setState({
                data: city
            })
        }
    
        //滑动到
        scrollTo(index) {
            this.sectionListRef.scrollToLocation({
                animated: true,
                sectionIndex: index,
                itemIndex: 0,
                viewPosition: 0,
                viewOffset: ITEM_HEIGHT
            });
        }
    
        //右侧城市首字母列表
        renderLetters(letter, index) {
            return (
                <TouchableOpacity key={index} activeOpacity={0.6} onPress={() => {
                    this.scrollTo(index)
                }}>
                    <Text style={styles.letterText}>{letter}</Text>
                </TouchableOpacity>
            )
        }
    
        //城市Item
        renderCityItem(item) {
            return (
                <TouchableOpacity activeOpacity={0.6} onPress={() => {
                    ToastAndroid.show(item, ToastAndroid.SHORT)
                }}>
                    <Text style={styles.item}>{item}</Text>
                </TouchableOpacity>
            )
        }
    
        render() {
            return (
                <View style={styles.container}>
                    <SectionList
                        sections={this.state.data}
                        initialNumToRender={383}
                        ref={ref => (this.sectionListRef = ref)}
                        renderItem={({item}) => this.renderCityItem(item)}
                        renderSectionHeader={({section}) => <Text style={styles.sectionHeader}>{section.key}</Text>}
                        keyExtractor={(item, index) => index}
                        refreshing={false}
                        getItemLayout={this.getItemLayout}
                        stickySectionHeadersEnabled={true}
                        showsVerticalScrollIndicator={false}
                    />
                    <View style={styles.letters}>
                        {letters.map((letter, index) => this.renderLetters(letter, index))}
                    </View>
                </View>
            )
                ;
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1
        },
        sectionHeader: {
            paddingTop: 5,
            paddingLeft: 10,
            paddingRight: 10,
            paddingBottom: 5,
            fontSize: 18,
            fontWeight: 'bold',
            color: '#000',
            height: ITEM_HEIGHT,
            backgroundColor: '#8bc9ff',
        },
        item: {
            padding: 10,
            fontSize: 18,
            height: ITEM_HEIGHT,
        }, letters: {
            position: 'absolute',
            top: 0,
            bottom: 0,
            right: 10,
            backgroundColor: 'transparent',
            justifyContent: 'center',
            alignItems: 'center',
        }, letterText: {
            padding: 2,
            fontSize: 13,
        }
    });
    
    

    城市列表

    来源:https://blog.csdn.net/kimi985566/article/details/85088889

  • 相关阅读:
    Django----图片验证码接口
    jwt安装配置
    jwt介绍
    课程章节页面
    git使用整理
    时间复杂度和空间复杂度,排序算法
    linux源码安装mysql,shell编程学习,ubuntu
    linux之任务调度,磁盘分区,yum下载
    linux常用命令修改权限查看文档
    linux用户组相关,密码相关,文件操作,和启动级别
  • 原文地址:https://www.cnblogs.com/qixidi/p/10186101.html
Copyright © 2020-2023  润新知