1.需求
使用ReactNative 的 bottom-tabs组件搭建一个App端框架,在实践的过程中,遇到了几个小问题,特此记录下。
项目需要安装以下组件。
"react": "17.0.2",
"react-native": "0.66.3",
"@react-navigation/bottom-tabs": "^6.2.0",
"@react-navigation/native": "^6.0.8",
"@react-navigation/native-stack": "^6.5.0",
"react-native-screens": "^3.12.0"
2.效果如下
3.编码
项目结构如下,页面全部放置在了src/view目录下。
3.1欢迎页
一般App都会有欢迎页面,这里我的做法是在App注册的时候,直接指向欢迎页面。
如果项目不需要欢迎页,可以在App注册时,直接指向Nav.js
import {AppRegistry} from 'react-native';
import Welcome from './src/view/welcome';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => Welcome);
欢迎页代码如下
主要逻辑是在欢迎页显示 2秒后,自动加载Nav.js,Nav文件里面是具体的底部Tab导航的实现以及项目的主要架构。
import React, {Component} from 'react';
import {ImageBackground, Text, View,
StatusBar,
} from 'react-native';
import Nav from './Nav'
export default class Welcome extends Component{
constructor(props){
super(props);
this.state = {
start: false,
}
setTimeout(()=>{
this.setState({start: true})
},2000)
}
render(){
return(
<View style={{flex:1}}>
{ this.state.start?<Nav/>:
<View style={{width: '100%',height:'100%'}}>
<StatusBar
animated={false} //指定状态栏的变化是否应以动画形式呈现。目前支持这几种样式:backgroundColor, barStyle和hidden
hidden={false} //是否隐藏状态栏。
networkActivityIndicatorVisible={false}//仅作用于ios。是否显示正在使用网络。
showHideTransition={'fade'}//仅作用于ios。显示或隐藏状态栏时所使用的动画效果(’fade’, ‘slide’)。
backgroundColor='rgba(255,255,255,0)'// {'transparent'} //状态栏的背景色
translucent={true}//指定状态栏是否透明。设置为true时,应用会在状态栏之下绘制(即所谓“沉浸式”——被状态栏遮住一部分)。常和带有半透明背景色的状态栏搭配使用。
barStyle={'light-content'} // enum('default', 'light-content', 'dark-content')
/>
<ImageBackground source={require('../image/green.jpg')}
style={{width: '100%',height:'100%',justifyContent:'center',alignItems:'center'}} >
<Text style={{fontSize:20,color:'blank'}}>Welcome</Text>
</ImageBackground>
</View>
}
</View>
)
}
}
3.2 主体架构Nav文件
这里耗时比较久的是进入二级页面需要隐藏底部 Tab栏,比如进入详情页,需要隐藏底部的Tab导航栏,这里使用了自定义的组件(MyTabBar方法)来实现底部 Tab的隐藏和显示
Nav.js完整代码如下
import * as React from 'react';
import { Text, View ,StyleSheet,Image, StatusBar,
TouchableOpacity
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeView from './index/home'
import DetailView from './index/detail'
import SettingView from './setting/index'
//头部导航栏的配置
const headerOpt ={
headerStyle: {
backgroundColor: '#ED5100',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
},
}
const HomeStack = createNativeStackNavigator();
function HomeStackScreen() {
//headerShown 是否显示顶部标题栏
return (
<HomeStack.Navigator
>
<HomeStack.Screen options={{headerShown:false }} name="Home" component={HomeView} />
<HomeStack.Screen options={headerOpt} name="Detail" component={DetailView} />
</HomeStack.Navigator>
);
}
const SettingStack = createNativeStackNavigator();
function SettingStackScreen(){
return(
<SettingStack.Navigator>
<SettingStack.Screen name="Setting" component={SettingView} options={headerOpt}/>
</SettingStack.Navigator>
)
}
function renderBar(state,navigation){
var array = [];
let iconName = '';
let title = '';
const {routes,index} = state;
let showTab = true; //是否显示底部 tab
for(let i=0; i<routes.length; i++){
const route = routes[i];
const focused = state.index === i;
if (route.name === 'HomeS') {
iconName = focused
? require('../image/icon_home.png')
: require('../image/icon_home_sel.png');
title = "首页";
} else if (route.name === 'SettingS') {
iconName = focused ? require('../image/icon_tabbar_misc_selected.png')
: require('../image/icon_tabbar_misc.png');
title = "设置";
}
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!focused && !event.defaultPrevented) {
navigation.navigate({ name: route.name, merge: true });
}
};
if((route.state && route.state.routes.length >1) ){
showTab = false;
}
array.push(<TouchableOpacity style={styles.barStyle} onPress={onPress} key={route.name}>
<Image style={[styles.tabIcon,{width:20,height:20}]} source={iconName}/>
<Text style={{color:focused?'#ED5100':'#aaa'}}>{title}</Text>
</TouchableOpacity>)
}
return showTab?array:null;
}
function MyTabBar(props) {
const { state, descriptors, navigation } = props;
return (
<View style={{ flexDirection: 'row',justifyContent:'space-around' }}>
{renderBar(state,navigation)
}
</View>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<View style={{ flex: 1,}}>
<StatusBar
animated={false} //指定状态栏的变化是否应以动画形式呈现。目前支持这几种样式:backgroundColor, barStyle和hidden
hidden={false} //是否隐藏状态栏。
networkActivityIndicatorVisible={false}//仅作用于ios。是否显示正在使用网络。
showHideTransition={'fade'}//仅作用于ios。显示或隐藏状态栏时所使用的动画效果(’fade’, ‘slide’)。
backgroundColor='rgba(255,255,255,0)'// {'transparent'} //状态栏的背景色
translucent={true}//指定状态栏是否透明。设置为true时,应用会在状态栏之下绘制(即所谓“沉浸式”——被状态栏遮住一部分)。常和带有半透明背景色的状态栏搭配使用。
barStyle={'light-content'} // enum('default', 'light-content', 'dark-content')
/>
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
tabBar={props => <MyTabBar {...props} />}
/** */
screenOptions={({ route }) => ({
tabBarActiveTintColor: '#ED5100',
tabBarInactiveTintColor: '#aaa',
})}>
<Tab.Screen options={{headerShown:false}} name="HomeS" component={HomeStackScreen} />
<Tab.Screen options={{headerShown:false}} name="SettingS" component={SettingStackScreen} />
</Tab.Navigator>
</NavigationContainer>
</View>
);
}
const styles = StyleSheet.create({
barStyle:{
height: 55,
justifyContent:'center',
alignItems:'center',
},
tabIcon:{
width:24,
height:24,
resizeMode:'contain'
},
});
3.3首页
首页非常简单,没什么好说的,代码如下
import React from 'react';
import {
Text,
View,
Button,
} from 'react-native';
export default class Home extends React.Component {
constructor(props){
super(props);
}
onPress(){
const {navigation} = this.props;
if(navigation){
navigation.push('Detail')
}
}
render(){
return(
<View style={{backgroundColor:'#77ffee',flex:1,justifyContent:'center',alignItems:'center'}}>
<Text>首页</Text>
<Button onPress={()=>this.onPress()}
title="点击进入详情页"></Button>
</View>
)
}
}
3.4 详情页
代码如下
import React from 'react';
import {
Text,
View,
} from 'react-native';
export default class Setting extends React.Component {
constructor(props){
super(props);
props.navigation.setOptions({ title:'详情'});
}
render(){
return(
<View style={{backgroundColor:'#fff',flex:1,justifyContent:'center',alignItems:'center'}}>
<Text>详情页</Text>
</View>
)
}
}
3.5 设置页
设置页面右上角(顶部导航栏右边)加了一个小按钮,这里主要记录下导航栏按钮的使用方法。
代码如下
import React from 'react';
import {
View,
Image,
TouchableOpacity,
ToastAndroid,
} from 'react-native';
export default class Setting extends React.Component {
constructor(props){
super(props);
props.navigation.setOptions({ title:'设置'});
props.navigation.setOptions({
headerRight:()=>
<TouchableOpacity onPress={()=>this._add()}>
<Image style={{width:20,height:20}} source={require('../../image/add.png')}/>
</TouchableOpacity>
})
}
_add=()=>{
ToastAndroid.show("点击右上角",ToastAndroid.SHORT)
}
render(){
return(
<View style={{backgroundColor:'#fff',flex:1,}}>
</View>
)
}
}