react--动态添加tab和tab对应的界面

react--动态添加tab和tab对应的界面

情景描述
产品丢过来一个分类的功能:分几个类不固定,分类中的数据是一个列表,可分页、刷新。分析一下,数据不固定,就需要动态获取,大分类数据(有多少个分类)和分类中的列表数据都需要从接口中获取,这是一个比较常用的功能。

先上效果图:

uu81y-x1sv4.gif

图中横向滑动的数据、分类下面的列表数据都是从接口得到的,要达到这样的效果,我们先分析一下,上面横向滑动的分类是一个scrollTab,react中有这样的开源项目,react-native-scrollable-tab-view(如果该项目可以满足你的需求,请忽略下面的内容)。下面的列表可以用FlatList来做,因为数据结构、页面样式相似,我们可以用一个界面就能完成。

分析完毕,下面我们一步一步来做,最终达成我们想要的效果。

第一步:导入react-native-scrollable-tab-view

使用npm将该控件导入到项目中:
npm install --save react-native-scrollable-tab-view
运行项目(运行Android为例):
react-native run-android

第二步:使用react-native-scrollable-tab-view

下面的代码是对ScrollableTabView的简单使用,更多属性见 react-native-scrollable-tab-view,其中
renderTabBar={() => <ClassificationTabBar tabNames={this._renderTabTitle()}/>}
{this._renderDynamicView()}会作具体说明,以下是ScrollableTabView的简单使用代码示例:

render() {
    return <View style={styles.container}>
                <ScrollableTabView
                    ref={(ScrollableTabView) => {
                        _scrollableTabView = ScrollableTabView;
                    }}//将该控件提取出来,_scrollableTabView指向该控件
                    renderTabBar={() => <ClassificationTabBar tabNames={this._renderTabTitle()}/>}//也可以使用DefaultTabBar,ScrollableTabBar
                    initialPage={0}//初始化哪一个界面
                    locked={false}//是否不允许滑动
                    scrollWithoutAnimation={true}
                    showsHorizontalScrollIndicator={false}
                    prerenderingSiblingsNumber={1}
                    tabBarActiveTextColor={'#cba000'}
                    tabBarInactiveTextColor={'#777'}>
                    {this._renderDynamicView()}
                </ScrollableTabView>
            </View>
}

上述代码中使用到了ClassificationTabBar ,ScrollableTabView中提供了DefaultTabBar,ScrollableTabBar,如果这两个可以满足需求,也可以使用这两个控件,跳过该步骤。ClassificationTabBar 中可自行定义tab样式风格,更具扩展性,以下是ClassificationTabBar 的全部代码,可直接使用:

/**
 * @author zhousx
 *
 * description:可滑动的动态 tabView
 */

import React, {Component} from 'react';
import {
    AppRegistry, Dimensions,
    StyleSheet,
    Text,
    TouchableOpacity,
    View
} from 'react-native';
import PropTypes from 'prop-types'
import {DEFAULT_FONT} from "../utils/FontUtils";
let {width, height} = Dimensions.get('window');

export default class ClassificationTabBar extends Component {
    static propTypes = {

        activeTab: PropTypes.number, // 当前被选中的tab下标
        tabs: PropTypes.array, // 所有tabs集合

        tabNames: PropTypes.array, // 保存Tab名称
    };  // 注意这里有分号


    render() {
        return (
            <View style = {styles.parent}>
                <View style={styles.tabs}>
                    {/*遍历。系统会提供一个tab和下标 调用一个自定义的方法*/}
                    {this.props.tabs.map((tab, i) => this.renderTabOption(tab, i))}
                </View>
                <View style = {{backgroundColor:'#fafafa',width:width,height:1}}/>
            </View>
        );
    }


///  处理tabbar的颜色和字体及图标
    renderTabOption(tab, i) {
        let color = this.props.activeTab === i ? "#cba000" : "#aaaaaa"; // 判断i是否是当前选中的tab,设置不同的颜色
        let color2 = this.props.activeTab === i ? "#cba000" : "#fff"; // 判断i是否是当前选中的tab,设置不同的颜色
        return (
            //因为要有点击效果 所以要引入可触摸组件
            <TouchableOpacity onPress={() => this.props.goToPage(i)} style={styles.tab} key={tab}>
                <View style={styles.tabItem}>
                    <Text style={{color: color, fontFamily: DEFAULT_FONT, marginTop: 15}}>
                        {this.props.tabNames[i]}
                    </Text>
                    <Text
                        style={{backgroundColor: color2, width: 40, height: 2, marginTop: 10}}/>
                </View>
            </TouchableOpacity>
        );
    }


}

const styles = StyleSheet.create({
    parent:{
        elevation: 2,
        shadowOffset: {
            width: 1, height: 2
        },
    },
    tabs: {
        flexDirection: 'row',
        height: 45,
    },

    tab: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },

    tabItem: {
        flexDirection: 'column',
        alignItems: 'center',
        width: 100
    },

});

ClassificationTabBar 类中用到了prop-types,这是一个属性校验的类,在react老的版本中不需要导入,在新版本中被分离出来,是一个呗广泛使用的工具,如果项目中导入了请忽略,如果没导入请使用npm下载:
npm install --save prop-types
其中

static propTypes = {

        activeTab: PropTypes.number, // 当前被选中的tab下标
        tabs: PropTypes.array, // 所有tabs集合

        tabNames: PropTypes.array, // 保存Tab名称
    };  // 注意这里有分号

tabNames为传递过来的tab的标题数组,也就是ScrollableTabView中传递过去的值:
renderTabBar={() => <ClassificationTabBar tabNames={this._renderTabTitle()}/>}
this._renderTabTitle()方法:

_renderTabTitle() {
        const texts = [];
        for (let i = 0; i < this.state.dataArray.length; i++) {
            const labelName = this.state.dataArray[i].typeName;
            texts.push(labelName);
        }
        return texts;
    }

this.state.dataArray是在请求完成之后set的值。

到此,tab已动态设置完成,接下来需要动态添加列表页面。

第三步:动态添加列表页面

动态添加的类表页面的个数需要根据请求返回的分类的个数来确定,所以需要遍历分类个数来动态添加列表界面,因为我这边列表界面的样式都相同,所以把列表界面封装为一个Compoent,然后将参数传给列表界面,多个样式道理也相同。
下面是动态添加界面的代码:

_renderDynamicView() {
        const _views = [];
        for (let i = 0; i < this.state.dataArray.length; i++) {
            const labelName = this.state.dataArray[i].typeName;
            const classificationId = this.state.dataArray[i].id;
            console.log('labelName = ' + labelName);
            _views.push(<View
                style={styles.container} tabLabel={labelName} key={i}>
                <SecondClassificationView classificationId={classificationId} {...this.props}/>
            </View>);
        }
        return _views;
    }

将分类id传递给Component,在这里也就是SecondClassificationView ,然后通过ID去获取不同的数据,SecondClassificationView 是一个简单的继承Component的控件:

以下是SecondClassificationView 的部分代码:

export default class SecondClassificationView extends Component<props> {

    static propTypes = {
        classificationId: PropsTypes.number,
    };

    constructor(props) {
        super(props);
        this.state = {
            dataArray: [],
            isRefreshing: false
        }
    }

render() {
        const {navigation} = this.props;
        return <View
            style={styles.container}>
            <FlatList
                data={this.state.dataArray}
                renderItem={
                    (data) =>
                        <TouchableOpacity activeOpacity={0.5}
                                          onPress={() => {
                                              
                                          }}>
                            <View style={styles.item}>
                                <Image
                                    style={{marginLeft: 20, height: 18, width: 18,}}
                                    source={require('../../res/images/pic_book_head.png')}/>
                                <Text style={styles.text}>{data.item.bookName}</Text>
                            </View>
                        </TouchableOpacity>
                }
                ItemSeparatorComponent={
                    () => this._renderSeparator()
                }
                ListEmptyComponent={
                    () => this._renderEmpty()
                }
                refreshing={this.state.isRefreshing}
                onRefresh={
                    () => {
                        this._onRefresh()
                    }
                }
            />
        </View>
    }
}

至此,动态添加tab和tab对应界面的所有步骤就完成了,如果有问题的地方,请在下方留言,看到我会第一时间回复!
如果对你有帮助,点个赞呗!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • React-Native-Elements 一组开发RN的UI工具包(强烈推荐) 🔥🔥🔥🔥🔥 react-nati...
    以德扶人阅读 46,053评论 2 135
  • 背景 最近在做一个RN项目,需要做个tab的需求,查看了RN的官方文档,官方为我们提供的Tab控制器有两种:Vie...
    wfunny阅读 2,426评论 4 1
  • 持续更新中...... 一套企业级的 UI 设计语言和 React 实现。 https://mobile.ant....
    日不落000阅读 5,655评论 0 35
  • 能对你的工作坚持一辈子,是何种非凡! 捏了40年面团的手艺人-水上力 水上力,那个被英国权威美食评鉴指南《WHER...
    LLLL小姐阅读 594评论 0 0
  • 如果有一天,连我自己都不相信努力的意义,那我才会心甘情愿说放弃。可是经历的所有,都在告诉我,没关系,就继续走吧。经...
    七叶时语阅读 167评论 0 1