搭建react-native項(xiàng)目框架之集成第三方路由和tab頁(yè)
先來(lái)看下效果:
這里需要用到兩個(gè)第三方插件:
react-native-router-flux?路由,https://github.com/aksonov/react-native-router-flux
react-native-scrollable-tab-view 選項(xiàng)卡,https://github.com/skv-headless/react-native-scrollable-tab-view
prop-types?類型檢測(cè)庫(kù)
閱讀官方文檔,安裝這三個(gè)插件
$?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';
在工作目錄下創(chuàng)建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監(jiān)聽(tīng)范圍?[0,?tab數(shù)量-1] ????????this.props.scrollValue.addListener(CustomTabBar.setAnimationValue); ????} ????renderTabOption(tab,?i)?{ ????????let?color?=?this.props.activeTab?===?i???"#1296db"?:?"#707070";?//?判斷i是否是當(dāng)前選中的tab,設(shè)置不同的顏色 ????????let?tabName?=?this.props.tabNames[i]; ????????return?(this.props.goToPage(i)}?style={[styles.tab]}?key={'tab'?+?i}>{tabName}); ????} ????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?=?(); ????????????????????tabs.push(middle); ????????????????} ????????????????tabs.push(this.renderTabOption(tab,?i)); ????????????} ????????????return?tabs; ????????} ????} ????render()?{ ????????let?tabBarHeight?=?Platform.select({ ????????????ios:?Common.isIphoneX???68?:?49, ????????????android:?49, ????????}); ????????return?({this.renderTabs()}); ????} } CustomTabBar.propTypes?=?{ ????goToPage:?PropTypes.func,?//?跳轉(zhuǎn)到對(duì)應(yīng)tab的方法 ????activeTab:?PropTypes.number,?//?當(dāng)前被選中的tab下標(biāo) ????tabs:?PropTypes.array,?//?所有tabs集合 ????tabNames:?PropTypes.array,?//?保存Tab名稱 ????tabIconNames:?PropTypes.array,?//?保存Tab圖標(biāo) }; 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, ????}, });
這里用到了一個(gè)公共類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轉(zhuǎn)換成dp //以iphone6為基準(zhǔn),如果以其他尺寸為基準(zhǔn)的話,請(qǐng)修改下面的750和1334為對(duì)應(yīng)尺寸即可. const?width2x?=?750?/?DEFAULT_DENSITY; //px轉(zhuǎn)換成dp const?height2x?=?1334?/?DEFAULT_DENSITY; //?iPhoneX const?X_WIDTH?=?375; const?X_HEIGHT?=?812; /** ?*?設(shè)置字體的size(單位px) ?*?@param?size?傳入設(shè)計(jì)稿上的px ?*?@returns?{Number}?返回實(shí)際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,?//小時(shí) ????????"H+"?:?this.getHours(),?//小時(shí) ????????"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(); ????//字體自適應(yīng)大小 ????static?autoFontSize(size)?{ ????????return?autoFontSize(size); ????} ????//尺寸自適應(yīng)大小 ????static?autoScaleSize(size)?{ ????????return?autoScaleSize(size); ????} ????//獲取指定格式時(shí)間字符串 ????static?getDateFormat(date,?format?=?false)?{ ????????if?(false?!==?format)?{ ????????????format?=?"yyyy-MM-dd?HH:mm:ss"; ????????} ????????return?date.format(format); ????} }
然后在App.js內(nèi)渲染路由和tab,注意在路由外層要包一個(gè)View,以便后面我們使用全局的toast和loading,同時(shí)tab作為路由中的一個(gè)頁(yè)整體呈現(xiàn)。
//自定義組件 import?CustomTabBar?from?'./components/customTabBar';?//自定義選項(xiàng)卡 //選項(xiàng)卡Tab頁(yè) import?HomeTabScreen?from?'./views/home';?//首頁(yè) import?HeadsetTabScreen?from?'./views/headset';?//試聽(tīng) import?BoughtTabScreen?from?'./views/bought';?//已購(gòu) import?MineTabScreen?from?'./views/mine';?//我的 //頁(yè)面 import?SignInOrUpScreen?from?'./views/signIns/signInOrUp';?//免注冊(cè)登錄 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?=?['首頁(yè)',?'試聽(tīng)',?'已購(gòu)',?'我的']; ????????return?(} ????????????????tabBarPosition='bottom' ????????????>); ????} } export?default?class?App?extends?Component?{ ????render()?{ ????????return?({/*首頁(yè)(tab)*/}{/*登錄*/}); ????} } const?styles?=?StyleSheet.create({ ????router:?{ ????????backgroundColor:?'#e6e6e6', ????}, ????root:?{ ????????backgroundColor:?'#e6e6e6', ????}, ????title:?{ ????????color:?'#ffffff', ????}, });
這樣就把路由和tab集成到一起了,關(guān)于路由的使用這里有個(gè)詳解《react-native-router-flux使用技巧(API篇)》,我這里就說(shuō)一些簡(jiǎn)單的跳轉(zhuǎn)。
Actions.pop()
返回上一頁(yè)。
Actions.key()
不帶參跳轉(zhuǎn)到鍵為key的頁(yè),Actions.key()中的key即為Scene的key屬性值,如代碼中的home和signInOrUp。
Actions.key(param)
帶參數(shù)跳轉(zhuǎn)到鍵為key的頁(yè),Actions.key()中的key即為Scene的key屬性值,如代碼中的home和signInOrUp。
參數(shù)可以通過(guò)屬性獲取,如this.props.param。