【搭建react-native项目框架】3.集成第三方路由和tab页

先来看下效果:


这里需要用到两个第三方插件:

react-native-router-flux 路由,https://github.com/aksonov/react-native-router-flux

react-native-scrollable-tab-view 选项卡,https://github.com/skv-headless/react-native-scrollable-tab-view

prop-types 类型检测库

阅读官方文档,安装这三个插件

$ npm install react-native-router-flux --save
$ npm install react-native-scrollable-tab-view --save
$ npm install prop-types --save

在App.js中引入插件

import { Router, Scene } from 'react-native-router-flux';
import ScrollableTabView from 'react-native-scrollable-tab-view';

在工作目录下创建components文件夹,并添加customTabBar.js文件作为tab栏,如下

import React, {Component} from 'react';
import {
    Platform,
    StyleSheet,
    StatusBar,
    View,
    TouchableOpacity,
    Image,
    Text,
} from 'react-native';
//第三方插件
import PropTypes from 'prop-types';
//自定义组件
import Common from './common'; //公共类

const tabIcons = [
    require('../resources/images/tabs/home_v.png'),
    require('../resources/images/tabs/home_x.png'),
    require('../resources/images/tabs/headset_v.png'),
    require('../resources/images/tabs/headset_x.png'),
    require('../resources/images/tabs/bought_v.png'),
    require('../resources/images/tabs/bought_x.png'),
    require('../resources/images/tabs/mine_v.png'),
    require('../resources/images/tabs/mine_x.png')
];

export default class CustomTabBar extends Component {
    constructor(props) {
        super(props);
    }

    static setAnimationValue({value}) {
        console.log(value);
    }

    componentDidMount() {
        // Animated.Value监听范围 [0, tab数量-1]
        this.props.scrollValue.addListener(CustomTabBar.setAnimationValue);
    }

    renderTabOption(tab, i) {
        let color = this.props.activeTab === i ? "#1296db" : "#707070"; // 判断i是否是当前选中的tab,设置不同的颜色
        let tabName = this.props.tabNames[i];
        return (
            <TouchableOpacity onPress={()=>this.props.goToPage(i)} style={[styles.tab]} key={'tab' + i}>
                <View style={[styles.tabBox]}>
                    <Image
                        source={tabIcons[this.props.activeTab === i ? i*2 : i*2+1]}
                        style={[styles.tabBoxIcon]}
                    />
                    <Text style={[styles.tabBoxName, {color: color}]}>
                        {tabName}
                    </Text>
                </View>
            </TouchableOpacity>
        );
    }

    renderTabs() {
        if (true !== this.props.placeMiddle || 0 !== this.props.tabs.length%2) {
            return this.props.tabs.map((tab, i) => this.renderTabOption(tab, i));
        } else  {
            let tabs = [];
            for (let i = 0; i < this.props.tabs.length; i++) {
                let tab = this.props.tabs[i];
                if (i === parseInt(this.props.tabs.length/2)) {
                    let middle = (
                        <View key={'tabMiddle'} style={[styles.tab]}>
                            <View style={[styles.tabMiddleBox]}/>
                        </View>
                    );
                    tabs.push(middle);
                }
                tabs.push(this.renderTabOption(tab, i));
            }
            return tabs;
        }
    }

    render() {
        let tabBarHeight = Platform.select({
            ios: Common.isIphoneX ? 68 : 49,
            android: 49,
        });
        return (
            <View key={'custom'} style={[styles.tabs, {height: tabBarHeight}]}>
                <StatusBar
                    backgroundColor="#ffffff"
                    barStyle="dark-content"
                />
                {this.renderTabs()}
            </View>
        );
    }
}

CustomTabBar.propTypes = {
    goToPage: PropTypes.func, // 跳转到对应tab的方法
    activeTab: PropTypes.number, // 当前被选中的tab下标
    tabs: PropTypes.array, // 所有tabs集合
    tabNames: PropTypes.array, // 保存Tab名称
    tabIconNames: PropTypes.array, // 保存Tab图标
};

const styles = StyleSheet.create({
    tabs: {
        flexDirection: 'row',
        backgroundColor:'#ffffff',
        borderTopWidth: 0.5,
        borderTopColor: '#cdcdcd',
    },
    tab: {
        flex: 1,
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
    },
    tabBox: {
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
        width: 48,
        height: 48,
    },
    tabMiddleBox: {
        flex: 1,
        flexDirection: 'column',
        justifyContent: 'center',
        alignItems: 'center',
        width: 48,
        height: 48,
    },
    tabBoxIcon: {
        width: 22,
        height: 22,
    },
    tabBoxName: {
        fontSize: 10,
        marginTop: 3,
    },
});
这里用到了一个公共类common.js
import {
    Dimensions,
    PixelRatio,
    Platform
} from 'react-native';


export let screenWidth = Dimensions.get('window').width;
export let screenHeight = Dimensions.get('window').height;
export let fontScale = PixelRatio.getFontScale();
export let pixelRatio = PixelRatio.get();


//像素密度
export const DEFAULT_DENSITY = 2;
//px转换成dp
//以iphone6为基准,如果以其他尺寸为基准的话,请修改下面的750和1334为对应尺寸即可.
const width2x = 750 / DEFAULT_DENSITY;
//px转换成dp
const height2x = 1334 / DEFAULT_DENSITY;
// iPhoneX
const X_WIDTH = 375;
const X_HEIGHT = 812;


/**
 * 设置字体的size(单位px)
 * @param size 传入设计稿上的px
 * @returns {Number} 返回实际sp
 */
export function autoFontSize(size) {
    let scaleWidth = screenWidth / width2x;
    let scaleHeight = screenHeight / height2x;
    let scale = Math.min(scaleWidth, scaleHeight);
    size = Math.round((size * scale + 0.5));
    return size / DEFAULT_DENSITY;
}


/**
 * 屏幕适配,缩放size
 * @param size
 * @returns {Number}
 */
export function autoScaleSize(size) {
    let scaleWidth = screenWidth / width2x;
    let scaleHeight = screenHeight / height2x;
    let scale = Math.min(scaleWidth, scaleHeight);
    size = Math.round((size * scale + 0.5));
    return size / DEFAULT_DENSITY;
}


/**
 * 判断是否为iphoneX
 * @returns {boolean}
 */
export function isIphoneX() {
    return (
        Platform.OS === 'ios' &&
        ((screenHeight === X_HEIGHT && screenWidth === X_WIDTH) ||
        (screenHeight === X_WIDTH && screenWidth === X_HEIGHT))
    )
}


Date.prototype.format=function(fmt) {
    let o = {
        "M+" : this.getMonth()+1, //月份
        "d+" : this.getDate(), //日
        "h+" : this.getHours()%12 === 0 ? 12 : this.getHours()%12, //小时
        "H+" : this.getHours(), //小时
        "m+" : this.getMinutes(), //分
        "s+" : this.getSeconds(), //秒
        "q+" : Math.floor((this.getMonth()+3)/3), //季度
        "S" : this.getMilliseconds() //毫秒
    };
    let weekday = {
        "0" : "/u65e5",
        "1" : "/u4e00",
        "2" : "/u4e8c",
        "3" : "/u4e09",
        "4" : "/u56db",
        "5" : "/u4e94",
        "6" : "/u516d"
    };
    if(/(y+)/.test(fmt)){
        fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
    }
    if(/(E+)/.test(fmt)){
        fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? "/u661f/u671f" : "/u5468") : "")+weekday[this.getDay()+""]);
    }
    for(let k in o){
        if(new RegExp("("+ k +")").test(fmt)){
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length===1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
        }
    }
    return fmt;
};


export default class Common {
    static SCREEN_WIDTH = screenWidth;
    static SCREEN_HEIGHT = screenHeight;
    static PIXEL_RATIO = pixelRatio;
    static DEFAULT_DENSITY = DEFAULT_DENSITY;


    //是否为iphoneX
    static isIphoneX = isIphoneX();


    //字体自适应大小
    static autoFontSize(size) {
        return autoFontSize(size);
    }


    //尺寸自适应大小
    static autoScaleSize(size) {
        return autoScaleSize(size);
    }


    //获取指定格式时间字符串
    static getDateFormat(date, format = false) {
        if (false !== format) {
            format = "yyyy-MM-dd HH:mm:ss";
        }
        return date.format(format);
    }
}

然后在App.js内渲染路由和tab,注意在路由外层要包一个View,以便后面我们使用全局的toast和loading,同时tab作为路由中的一个页整体呈现。

//自定义组件
import CustomTabBar from './components/customTabBar'; //自定义选项卡
//选项卡Tab页
import HomeTabScreen from './views/home'; //首页
import HeadsetTabScreen from './views/headset'; //试听
import BoughtTabScreen from './views/bought'; //已购
import MineTabScreen from './views/mine'; //我的
//页面
import SignInOrUpScreen from './views/signIns/signInOrUp'; //免注册登录
import SignInScreen from './views/signIns/signIn'; //登录

// const instructions = Platform.select({
//   ios: 'Press Cmd+R to reload,\n' +
//     'Cmd+D or shake for dev menu',
//   android: 'Double tap R on your keyboard to reload,\n' +
//     'Shake or press menu button for dev menu',
// });

export class Tabs extends Component {
    constructor(props) {
        super(props);
    }

    componentWillMount() {
        // Disable back button by just returning true instead of Action.pop()
        BackHandler.addEventListener('hardwareBackPress', () => {return true});
    }

    render() {
        let tabNames = ['首页', '试听', '已购', '我的'];
        return (
            <ScrollableTabView
                initialPage={0} //初始tab索引
                renderTabBar={() =>
                    <CustomTabBar
                        tabNames={tabNames} //tab名称
                        placeMiddle={false} //中间是否占位,即中间是否需要用特殊按钮样式等
                    />
                }
                tabBarPosition='bottom'
            >
                <HomeTabScreen key='homeTab' tabLabel='home' />

                <HeadsetTabScreen key='headsetTab' tabLabel='headset' />

                <BoughtTabScreen key='boughtTab' tabLabel='bought' />

                <MineTabScreen key='mineTab' tabLabel='mine' />
            </ScrollableTabView>
        );
    }
}

export default class App extends Component {
    render() {
        return (
            <View style={[{flex: 1, backgroundColor: 'red'}]}>
                <Router sceneStyle={[styles.router]}>
                    <Scene
                        key="root"
                        navigationBarStyle={[styles.root]}
                        titleStyle={[styles.title]}
                        headerMode="screen"
                    >
                        {/*首页(tab)*/}
                        <Scene
                            key="home"
                            component={Tabs}
                            title="首页"
                            initial={true}
                            hideNavBar={true} //此处以及其他页都隐藏了导航,我打算自定义组件作为头部导航栏。
                        />
                        {/*登录*/}
                        <Scene key="signInOrUp" component={SignInOrUpScreen} title="免注册登录" hideNavBar={true} />
                        <Scene key="signIn" component={SignInScreen} title="登录" hideNavBar={true} />
                    </Scene>
                </Router>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    router: {
        backgroundColor: '#e6e6e6',
    },
    root: {
        backgroundColor: '#e6e6e6',
    },
    title: {
        color: '#ffffff',
    },
});

这样就把路由和tab集成到一起了,关于路由的使用这里有个详解《react-native-router-flux使用技巧(API篇)》,我这里就说一些简单的跳转。

Actions.pop()

返回上一页。

Actions.key()

不带参跳转到键为key的页,Actions.key()中的key即为Scene的key属性值,如代码中的home和signInOrUp。

Actions.key(param)

带参数跳转到键为key的页,Actions.key()中的key即为Scene的key属性值,如代码中的home和signInOrUp。

参数可以通过属性获取,如this.props.param。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

公西雒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值