当前位置 : 主页 > 网络编程 > JavaScript >

React Native系列之Recyclerlistview使用详解

来源:互联网 收集:自由互联 发布时间:2023-02-08
目录 recyclerlistview的介绍与使用 1.安装 2.概述和功能 3. RecyclerListView的使用 1、dataProvider 2、LayoutProvider 3、rowRenderer 4、onEndReached 5、onEndReachedThreshold 6、extendedState 7、scrollViewProps RecyclerL
目录
  • recyclerlistview的介绍与使用
    • 1.安装
    • 2.概述和功能
    • 3. RecyclerListView的使用
      • 1、dataProvider
      • 2、LayoutProvider
      • 3、rowRenderer
      • 4、onEndReached
      • 5、onEndReachedThreshold
      • 6、extendedState
      • 7、scrollViewProps
  • RecyclerListView所有属性

    recyclerlistview的介绍与使用

    1.安装

    npm install --save recyclerlistview
    或者:
    yarn add recyclerlistview
    

    2.概述和功能

    RecyclerListView 是一个高性能的列表(listview)组件,同时支持 React NativeWeb ,并且可用于复杂的列表。RecyclerListView 组件的实现灵感,来自于 Android RecyclerView原生组件及iOS UICollectionView原生组件。

    RecyclerListView使用“cell recycling”来重用不再可见的视图来呈现项目,而不是创建新的视图对象。 对象的创建非常昂贵并且带有内存开销,这意味着当您滚动列表时内存占用量不断增加。 从内存中释放不可见的项目是另一种技术,但这会导致创建更多的对象和大量的垃圾收集。 回收是渲染无限列表的最佳方式,不会影响性能或内存效率。

    为什么需要RecyclerListView

    我们知道,React Native 的其他列表组件如ListView,会一次性创建所有的列表单元格——cell。如果列表数据比较多,则会创建很多的视图对象,而视图对象是非常消耗内存的。所以,ListView组件,对于我们业务中的这种无限列表,基本上是不可以用的。

    对于React Native 官方提供的高性能的列表组件FlatList, 在Android设备上的表现,并不是十分友好。它的实现原理,是将列表中不在可视区域内的视图,进行回收,然后根据页面的滚动,不断的渲染出现在可视区域内的视图。这里需要注意的是,FlatList是将不可见的视图回收,从内存中清除了,下次需要的时候,再重新创建。这就要求设备在滚动的时候,能快速的创建出需要的视图,才能让列表流畅的展现在用户面前。而问题也就出现在这里,Android设备因为老化等原因,计算力等跟不上,加之React Native 本身 JS 层与 Native 层之间交互的一些问题(这里不做深入追究),导致创建视图的速度达不到使列表流畅滚动的要求。

    那怎样来解决这样的问题呢?

    RecyclerListView 受到 Android RecyclerViewiOS UICollectionView 的启发,进行两方面的优化:

    • 仅创建可见区域的视图,这步与FlatList是一致的。
    • cell recycling,重用单元格,这个做法是FlatList缺乏的。

    对于程序来说,视图对象的创建是非常昂贵的,并且伴随着内存的消耗。意味着如果不断的创建视图,在列表滚动的过程中,内存占用量会不断增加。FlatList中将不可见的视图从内存中移除,这是一个比较好的优化手段,但同时也会导致大量的视图重新创建以及垃圾回收。

    RecyclerListView 通过对不可见视图对象进行缓存及重复利用,一方面不会创建大量的视图对象,另一方面也不需要频繁的创建视图对象和垃圾回收。

    基于这样的理论,所以RecyclerListView的性能是会优于FlatList的。

    3. RecyclerListView的使用

    属性:

    1、dataProvider

    首先需要定义一个数据驱动方法

    let dataProvider = new DataProvider((r1, r2) => {
        return r1 !== r2;
    })
    

    定义完成之后去初始化数据

    // 列表数据
        const [JRecyclerData, setJRecyclerData] = useState(_dataProvider.cloneWithRows(data));
    

    cloneWithRows

    • 想要更新列表的dataProvider数据也就是(DataSource)必须每次通过cloneWithRows这个来重新挂载datasource的值。
    • clone方法会自动提取新数据并进行逐行对比(使用rowHasChanged方法中的策略),这样列表就知道哪些行需要重新渲染了。

    2、LayoutProvider

    定义列表布局

    在这之前我们可以根据我们的业务场景,规划处几类的布局,然后自定义每种布局的类型来区分。

    //表示列表中会出现三种ui类型的item
    const ViewTypes = {
        FULL: 0,
        HALF_LEFT: 1,
        HALF_RIGHT: 2
    }
    

    下面就可以来区分布局了

    • 为了进行cell-recycling,RecyclerListView要求对每个cell(通常也叫Item)定义一个type,根据type设置cell的dim.widthdim.height
    //第一个函数是定义item的ui类型,第二个是定义item的高宽
    this._layoutProvider = new LayoutProvider(
        index => {
            if (index % 3 === 0) {
                return ViewTypes.FULL;
            } 
            ...
        },
        (type, dim) => {
            switch (type) {
                case ViewTypes.HALF_LEFT:
                    dim.width = width / 2;
                    dim.height = 160;
                    break;
                ...
            }
        }
    )
    

    3、rowRenderer

    rowRenderer负责渲染一个cell,同样是根据type来进行渲染:

    _rowRenderer(type, data) {
        switch (type) {
            case ViewTypes.HALF_LEFT:
                return (
                    <CellContainer style={styles.containerGridLeft}>
                        <Text>Data: {data}</Text>
                    </CellContainer>
                );
            ...
          }
    }
    

    例子:

    /***
     * To test out just copy this component and render in you root component
     */
    export default class RecycleTestComponent extends React.Component {
        constructor(args) {
            super(args);
    
            let { width } = Dimensions.get("window");
    
            //Create the data provider and provide method which takes in two rows of data and return if those two are different or not.
            let dataProvider = new DataProvider((r1, r2) => {
                return r1 !== r2;
            });
    
            //Create the layout provider
            //First method: Given an index return the type of item e.g ListItemType1, ListItemType2 in case you have variety of items in your list/grid
            //Second: Given a type and object set the height and width for that type on given object
            //If you need data based check you can access your data provider here
            //You'll need data in most cases, we don't provide it by default to enable things like data virtualization in the future
            //NOTE: For complex lists LayoutProvider will also be complex it would then make sense to move it to a different file
            this._layoutProvider = new LayoutProvider(
                index => {
                    if (index % 3 === 0) {
                        return ViewTypes.FULL;
                    } else if (index % 3 === 1) {
                        return ViewTypes.HALF_LEFT;
                    } else {
                        return ViewTypes.HALF_RIGHT;
                    }
                },
                (type, dim) => {
                    switch (type) {
                        case ViewTypes.HALF_LEFT:
                            dim.width = width / 2 - 0.0001;
                            dim.height = 160;
                            break;
                        case ViewTypes.HALF_RIGHT:
                            dim.width = width / 2;
                            dim.height = 160;
                            break;
                        case ViewTypes.FULL:
                            dim.width = width;
                            dim.height = 140;
                            break;
                        default:
                            dim.width = 0;
                            dim.height = 0;
                    }
                }
            );
    
            this._rowRenderer = this._rowRenderer.bind(this);
    
            //Since component should always render once data has changed, make data provider part of the state
            this.state = {
                dataProvider: dataProvider.cloneWithRows(this._generateArray(300))
            };
        }
    
        _generateArray(n) {
            let arr = new Array(n);
            for (let i = 0; i < n; i++) {
                arr[i] = i;
            }
            return arr;
        }
    
        //Given type and data return the view component
        _rowRenderer(type, data) {
            //You can return any view here, CellContainer has no special significance
            switch (type) {
                case ViewTypes.HALF_LEFT:
                    return (
                        <CellContainer style={styles.containerGridLeft}>
                            <Text>Data: {data}</Text>
                        </CellContainer>
                    );
                case ViewTypes.HALF_RIGHT:
                    return (
                        <CellContainer style={styles.containerGridRight}>
                            <Text>Data: {data}</Text>
                        </CellContainer>
                    );
                case ViewTypes.FULL:
                    return (
                        <CellContainer style={styles.container}>
                            <Text>Data: {data}</Text>
                        </CellContainer>
                    );
                default:
                    return null;
            }
        }
    
        render() {
            return <RecyclerListView layoutProvider={this._layoutProvider} dataProvider={this.state.dataProvider} rowRenderer={this._rowRenderer} />;
        }
    }
    const styles = {
        container: {
            justifyContent: "space-around",
            alignItems: "center",
            flex: 1,
            backgroundColor: "#00a1f1"
        },
        containerGridLeft: {
            justifyContent: "space-around",
            alignItems: "center",
            flex: 1,
            backgroundColor: "#ffbb00"
        },
        containerGridRight: {
            justifyContent: "space-around",
            alignItems: "center",
            flex: 1,
            backgroundColor: "#7cbb00"
        }
    };
    

    页面效果:

    但是在实际的业务开发中肯定不会是这么简单的,一般都会用到分页,下拉刷新什么的,下面介绍几个比较常用的属性:

    4、onEndReached

    列表触底是触发,一般是用来做上拉加载更过数据的时候来使用的

    <RecyclerListView
        layoutProvider={this._layoutProvider}
        dataProvider={this.dataProvider.cloneWithRows(this.state.infoList)}
        rowRenderer={this._rowRenderer}
        onEndReached={this._onLoadMore}
    />
    

    5、onEndReachedThreshold

    列表距离底部多大距离时触发onEndReached的回调,这个填写的是具体的像素值,与FlatList是有区别的,FlatList填写的是百分比

    <RecyclerListView
        layoutProvider={this._layoutProvider}
        dataProvider={this.dataProvider.cloneWithRows(this.state.infoList)}
        rowRenderer={this._rowRenderer}
        onEndReached={this._onLoadMore}
        onEndReachedThreshold={50}
    />
    

    6、extendedState

    在更新目前列表渲染以外的数据时,可以使用此属性更新状态,以便绘制出新的列表,并且不再重新渲染以前的列表数据

    <RecyclerListView
        layoutProvider={this._layoutProvider}
        dataProvider={this.dataProvider.cloneWithRows(this.state.infoList)}
        rowRenderer={this._rowRenderer}
        onEndReached={this._onLoadMore}
        onEndReachedThreshold={50}
        extendedState={this.state}
    />
    

    7、scrollViewProps

    继承scrollView的属性,RecyclerListView本身是不具有刷新属性的,要想使用刷新功能,就可以继承scrollView的下拉刷新

    <RecyclerListView
        scrollViewProps={{
            refreshControl: (
                <RefreshControl
                    refreshing={this.state.loading}
                    onRefresh={async () => {
                        this.setState({ loading: true });
                        await this.getInfo();
                        this.setState({ loading: false });
                    }}
                />
            )
        }}
    />
    

    下面看一下完整的例子:

    import React, { Component } from "react";
    import { View, Text, Dimensions, StyleSheet, RefreshControl, Alert } from "react-native";
    import { RecyclerListView, DataProvider, LayoutProvider } from "recyclerlistview";
    import WBCST from "./../../rn-app";
    const ViewTypes = {
        FULL: 0
    };
    const { width } = Dimensions.get("window");
    const styles = StyleSheet.create({
        container: {
            flexDirection: "row",
            justifyContent: "space-between",
            // alignItems: "center",
            flex: 1,
            backgroundColor: "#fff",
            // borderWidth: 1,
            borderColor: "#dddddd",
            margin: 15,
            marginTop: 0,
            padding: 15
        },
        topicLeft: {
            width: width - 210,
            marginRight: 10
        },
        topicRight: {
            backgroundColor: "#f5f5f5",
            width: 140,
            height: 140,
            padding: 15
        },
        topicTitle: {
            color: "#000",
            fontSize: 16,
            fontWeight: "700",
            lineHeight: 28
        },
        topicContext: {
            color: "#999",
            fontSize: 12,
            lineHeight: 18,
            marginTop: 10
        },
        topicNum: {
            fontSize: 14,
            marginTop: 20
        },
        topicRightText: {
            fontSize: 14,
            color: "#666"
        }
    });
    export default class RecycleTestComponent extends Component {
        constructor(props) {
            super(props);
            this.dataProvider = new DataProvider((r1, r2) => {
                return r1 !== r2;
            });
            let { width } = Dimensions.get("window");
            this._layoutProvider = new LayoutProvider(
                (index) => {
                    return ViewTypes.FULL;
                },
                (type, dim) => {
                    dim.width = width;
                    dim.height = 190;
                }
            );
            this.state = {
                pagenum: 1,
                infoList: [],
                loading: false,
                isLoadMore: false
            };
        }
        getInfo = () => {
            let num = this.state.pagenum;
            let info = this.state.infoList;
            WBCST.getFetch("http://app.58.com/api/community/aggregatepage/tabs/topic", {
                pagesize: 20,
                pagenum: num
            }).then((res) => {
                if (res) {
                    let loadMore = false;
                    if (num == 1) {
                        if (res.data.questions.length == 20) {
                            loadMore = true;
                        }
                        this.setState({
                            isLoadMore: loadMore,
                            infoList: res.data.questions
                        });
                    } else {
                        // info.concat(res.data.questions);
                        if (res.data.questions.length < 20) {
                            loadMore = false;
                        } else {
                            loadMore = true;
                        }
                        this.setState({
                            isLoadMore: loadMore,
                            infoList: this.state.infoList.concat(res.data.questions)
                        });
                    }
                }
            });
        };
        _rowRenderer = (type, data) => {
            return (
                <View style={styles.container}>
                    <View style={styles.topicLeft}>
                        <Text numberOfLines={2} style={styles.topicTitle}>
                            {data.topic.title}
                        </Text>
                        <Text numberOfLines={2} style={styles.topicContext}>
                            {data.topic.context}
                        </Text>
                        <Text style={styles.topicNum}>
                            {data.topic.pn}
                            人参与此话题
                        </Text>
                    </View>
                    <View style={styles.topicRight}>
                        <Text style={styles.topicRightText}>{data.user.name}</Text>
                        <Text style={[{ marginTop: 10 }, styles.topicRightText]}>{data.title}</Text>
                    </View>
                </View>
            );
        };
        _renderFooter = () => {
            return (
                <View>
                    <Text>上拉加载更多</Text>
                </View>
            );
        };
        _onLoadMore = () => {
            // Alert.alert(JSON.stringify("num"));
            if (!this.state.isLoadMore) {
                return;
            }
            let num = this.state.pagenum;
            num = num + 1;
            this.setState(
                {
                    pagenum: num
                },
                () => {
                    // Alert.alert(JSON.stringify(num));
                    this.getInfo();
                }
            );
        };
        componentDidMount = () => {
            this.getInfo();
        };
        render() {
            return (
                <RecyclerListView
                    layoutProvider={this._layoutProvider}
                    dataProvider={this.dataProvider.cloneWithRows(this.state.infoList)}
                    rowRenderer={this._rowRenderer}
                    extendedState={this.state}
                    onEndReached={this._onLoadMore}
                    onEndReachedThreshold={50}
                    // renderFooter={this._renderFooter}
                    scrollViewProps={{
                        refreshControl: (
                            <RefreshControl
                                refreshing={this.state.loading}
                                onRefresh={async () => {
                                    this.setState({ loading: true });
                                    // analytics.logEvent("Event_Stagg_pull_to_refresh");
                                    await this.getInfo();
                                    this.setState({ loading: false });
                                }}
                            />
                        )
                    }}
                />
            );
        }
    }
    

    效果图:

    RecyclerListView所有属性

    以上就是React Native系列之Recyclerlistview使用详解的详细内容,更多关于React Native使用Recyclerlistview的资料请关注易盾网络其它相关文章!

    上一篇:Vue3实现九宫格抽奖的示例代码
    下一篇:没有了
    网友评论