反应本机多列FlatList插入标语

时间:2019-06-15 03:59:08

标签: react-native react-native-flatlist

我在React Native应用程序中使用多列FlatList来显示如下所示的项目(左图)。我正试图像其他许多应用一样将AdMob标语集成到应用中,然后将广告标语插入列表的中间,如下图所示(右图)。

据我所知,FlatList不支持这种现成的布局。我想知道什么是实现此功能的好习惯,并且不影响应用程序性能。

(请注意,当到达列表结尾时,该列表支持“按需刷新”和无限加载。)

预先感谢您的任何建议。

enter image description here

2 个答案:

答案 0 :(得分:5)

在这种情况下,我总是建议删除numColumns属性,并用一个自定义的渲染函数代替它,该函数可以自己处理列。

假设我们具有以下数据结构:

const DATA = 
[{ id: 1, title: "Item One"}, { id: 2, title: "Item Two"}, { id: 3, title: "Item Three"}, 
{ id: 4, title: "Item Four"}, { id: 5, title: "Item Five"}, { id: 6, title: "Item Six"}, 
{ id: 7, title: "Item Seven"}, { id:8, title: "Item Eight"}, { id: 9, title: "Item Nine"}, 
{ id: 10, title: "Item Ten"}, { id: 11, title: "Item eleven"}, 
{ id: 12, title: "Item Twelve"}, { id: 13, title: "Item Thirteen"}];

正如我所说,我们不使用numColumns属性,而是在重构数据,以便我们可以按需呈现列表。在这种情况下,我们希望有3列,而在6个项目之后我们要显示广告横幅。

数据修改:

  modifyData(data) { 
    const  numColumns = 3;
    const addBannerAfterIndex = 6;
    const arr = [];
    var tmp = [];
    data.forEach((val, index) => {
      if (index % numColumns == 0 && index != 0){
        arr.push(tmp);
        tmp = [];
      }
      if (index % addBannerAfterIndex == 0 && index != 0){
        arr.push([{type: 'banner'}]);
        tmp = [];
      }
      tmp.push(val);
    });
    arr.push(tmp);
    return arr; 
  }

现在我们可以呈现转换后的数据:

主要渲染功能:

render() {
    const newData = this.modifyData(DATA); // here we can modify the data, this is probably not the spot where you want to trigger the modification 
    return (
      <View style={styles.container}>
        <FlatList 
        data={newData}
        renderItem={({item, index})=> this.renderItem(item, index)}
        /> 
      </View>
    );
}

RenderItem功能:

我删除了一些内联样式以使其更加清晰。

renderItem(item, index) {
    // if we have a banner item we can render it here 
    if (item[0].type == "banner"){
      return (
         <View key={index} style={{width: WIDTH-20, flexDirection: 'row'}}>
        <Text style={{textAlign: 'center', color: 'white'}}> YOUR AD BANNER COMPONENT CAN BE PLACED HERE HERE </Text>
      </View>
      )
    }

    //otherwise we map over our items and render them side by side 
     const columns = item.map((val, idx) => {
      return (
        <View style={{width: WIDTH/3-20, justifyContent: 'center', backgroundColor: 'gray', height: 60, marginLeft: 10, marginRight: 10}} key={idx}>
          <Text style={{textAlign: 'center'}}> {val.title} </Text>
        </View>
      )
    });
    return (
      <View key={index} style={{width: WIDTH, flexDirection: 'row', marginBottom: 10}}>
      {columns}
      </View>
    )
  }

输出:

output

工作示例:

https://snack.expo.io/SkmTqWrJS

答案 1 :(得分:1)

我推荐这个漂亮的包裹 https://github.com/Flipkart/recyclerlistview

实际上,我们在应用程序中处理了数千个数据列表,flatlist能够很好地处理它,但是我们仍在寻找高性能的listview组件来产生平滑的渲染和高效的内存。我们偶然发现了这个包裹。相信我,太好了。

提到您的问题,此软件包具有开箱即用呈现多个视图的功能。它也有很好的文档。

因此,基本上,该程序包具有设置列表视图的三个重要步骤。

  • DataProvider-构造函数,用于定义每个的数据 元素
  • LayoutProvider-定义布局(高度)的构造函数 /宽度)
  • RowRenderer-就像平面列表中的renderItem属性一样。

基本代码如下:

import React, { Component } from "react";
import { View, Text, Dimensions } from "react-native";
import { RecyclerListView, DataProvider, LayoutProvider } from "recyclerlistview";

const ViewTypes = {
    FULL: 0,
    HALF_LEFT: 1,
    HALF_RIGHT: 2
};

let containerCount = 0;

class CellContainer extends React.Component {
    constructor(args) {
        super(args);
        this._containerId = containerCount++;
    }
    render() {
        return <View {...this.props}>{this.props.children}<Text>Cell Id: {this._containerId}</Text></View>;
    }
}


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

        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;
                        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"
    }
};

在LayoutProvider中,您可以基于索引返回多种类型的视图,也可以在数据数组中添加viewType对象,然后基于该对象呈现视图。

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

tl; dr:选中https://github.com/Flipkart/recyclerlistview,然后使用layoutProvider呈现不同的视图。

运行小吃:https://snack.expo.io/B1GYad52b