文章目录
tabbar底部导航栏
安装依赖包
需要安装四个依赖包(自己找适配自己RN的导航版本,我这里RN下面的依赖目前都是最新的,如下图所示)react-navigation/native 网站
yarn add @react-navigation/native
yarn add @react-navigation/bottom-tabs
yarn add react-native-screens react-native-safe-area-context
新建两个tabbar页面
这里我做的homeScreen.js和SettingsScreen.js
homeScreen.js
import {Text, View} from 'react-native';
export default function homeScreen() {
return (
<View>
<Text>123</Text>
</View>
);
}
SettingsScreen.js
import {Text, View} from 'react-native';
export default function SettingsScreen() {
return (
<View>
<Text>456</Text>
</View>
);
}
配置tabbar页面,这里新起个页面,例如我这里做个MyTabs.js
MyTabs.js
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import HomeScreen from './homeScreen'
import SettingsScreen from './SettingsScreen'
import { Image } from 'react-native';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} options={{tabBarIcon:({color,size})=>{
return ( // 底部栏图标
<Image style={{width:15,height:15}} source={require('./1212.png')}></Image>
)
}}}/>
<Tab.Screen name="Settings" component={SettingsScreen} options={{tabBarIcon:({color,size})=>{
return (
<Image style={{width:15,height:15}} source={require('./1212.png')}></Image>
)
}}}/>
</Tab.Navigator>
);
}
export default MyTabs
App.js内容如下
import { NavigationContainer } from '@react-navigation/native';
import MyTabs from './src/MyTabs';
function App() {
return (
<NavigationContainer>
<MyTabs />
</NavigationContainer>
);
}
export default App
项目结构如下:
效果图
非tabbar页面的导航
安装三个插件
yarn add @react-navigation/native 如果上面装过了这个就不用装了
yarn add @react-navigation/stack
yarn add react-native-gesture-handler
homeScreen.js
import {Button, Text, View} from 'react-native';
export default function homeScreen(params) {
console.log(params);
return (
<View>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Text>123</Text>
<Button title='点击跳转另一个页面' onPress={()=>{params.navigation.navigate('Setting',{name:'zhangsan'})}}></Button>
</View>
);
}
settingsScreen.js
import {Button, Text, View} from 'react-native';
export default function SettingsScreen(props) {
return (
<View>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Text>456</Text>
<Button title='返回到上一级' onPress={()=>{props.navigation.goBack()}}></Button>
</View>
);
}
配置导航路由页面MyTab.js
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import HomeScreen from './homeScreen';
import SettingsScreen from './SettingsScreen';
const Stack = createStackNavigator();
function MyTabs() {
return (
<Stack.Navigator> // screenOptions={{ headerShown: false }} 这里还有个参数可以隐藏上面的导航条,我这里没有加,如果有需要隐藏就加上,如果需要这个返回键又需要在导航条加样式的话,建议自己做个组件
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Setting" component={SettingsScreen} />
</Stack.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<MyTabs />
</NavigationContainer>
);
}
export default App;
App.js
import MyTabs from './src/MyTabs';
function App() {
return (
<MyTabs />
);
}
export default App
效果图
点击跳转后的效果图
跳转后的新页面可以通过props.route拿到路由参数
既有底部导航,也有页面导航
下载的依赖包就是上面两种所安装的所有的
示例代码
import {Text, StyleSheet, View, Button} from 'react-native';
import React, {Component} from 'react';
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {NavigationContainer} from '@react-navigation/native';
const Tab = createBottomTabNavigator();
function FeedScreen(props) {
// 跳转方法prop.navigation.navigate参数是路由名称也就是Stack.Screen name
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>HomeScreen</Text>
<Button
title={'跳到Profile页面'}
onPress={() => props.navigation.navigate('Profile')}
/>
<Button
title={'跳到Settings页面'}
onPress={() => props.navigation.navigate('Settings')}
/>
</View>
);
}
function MessagesScreen(props) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>NewsScreen</Text>
</View>
);
}
function ProfileScreen(props) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>ProfileScreen</Text>
</View>
);
}
function SettingsScreen(props) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>SettingsScreen</Text>
</View>
);
}
function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Messages" component={MessagesScreen} />
</Tab.Navigator>
);
}
const Stack = createStackNavigator();
export default class Index extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{headerShown: false}}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 40,
},
});
在app.js引入
import MyTabs from './src/MyTabs';
function App() {
return (
<MyTabs />
);
}
export default App
效果图
自定义底部导航栏样式(还是以上面代码为基准,给Home这个组件内的Tab.Navigator这个标签加个tabBar属性,这个指向的就是自定义标题栏)
import {Text, StyleSheet, View, Button, TouchableOpacity} from 'react-native';
import React, {Component} from 'react';
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {NavigationContainer} from '@react-navigation/native';
const Tab = createBottomTabNavigator();
function FeedScreen(props) {
// 跳转方法prop.navigation.navigate参数是路由名称也就是Stack.Screen name
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>HomeScreen</Text>
<Button
title={'跳到Profile页面'}
onPress={() => props.navigation.navigate('Profile')}
/>
<Button
title={'跳到Settings页面'}
onPress={() => props.navigation.navigate('Settings')}
/>
</View>
);
}
function MessagesScreen(props) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>NewsScreen</Text>
</View>
);
}
function ProfileScreen(props) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>ProfileScreen</Text>
</View>
);
}
function SettingsScreen(props) {
return (
<View style={[styles.container]}>
<Text style={[styles.text]}>SettingsScreen</Text>
</View>
);
}
let asaq = [
{
name: 'Messages',
},
{
name: 'Settings',
},
];
function TabBar({navigation, state}) {
console.log('props', navigation);
console.log('state', state);
// 下面这个就是自定义的tabbar样式,各位自行修改
return (
<View style={{height:100,flexDirection:'row',justifyContent:'space-around'}}>
{asaq.map((item, index) => {
return (
<TouchableOpacity
onPress={() => {
navigation.navigate(item.name);
}}>
<Text>{item.name}</Text>
</TouchableOpacity>
);
})}
</View>
);
}
function Home() {
//tabbar属性是自定义底部导航栏的样式
return (
<Tab.Navigator tabBar={props => <TabBar {...props} />}>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Messages" component={MessagesScreen} />
</Tab.Navigator>
);
}
const Stack = createStackNavigator();
export default class Index extends Component {
render() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{headerShown: false}}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
text: {
fontSize: 40,
},
});
上面这个自定义的导航可以直接在自定义的导航上面加样式,也可以使用绝对定位定位到任意位置
在我们某一页面我们不希望用户可以返回到上一个页面,但是发现即使使用了navigation.replace(‘路由名称’)也阻止不了用户左右滑动返回到上一个界面,本人亲测下面这个方法非常好使,各位可以试试
import React, { useEffect } from 'react';
import { BackHandler } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
const App = () => {
useEffect(() => {
// 使用BackHandler监听 Android 设备上的物理返回按钮(即硬件返回按钮)的按下事件。
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => {
// 在此处添加你的逻辑来阻止安卓上的返回事件
return true; // 返回 true 表示已经处理了返回事件,不会继续传递
}
);
return () => backHandler.remove();
}, []);
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
gestureEnabled: false, // 禁用IOS的左滑返回
}}
/>
{/* 其他屏幕 */}
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = () => {
return (
// 主屏幕内容
);
};
export default App;
无组件导航的案例
由于上面的导航都过于依赖组件,例如导航要依赖于hooks函数,通过hooks进行导航,或者直接使用组件导航到某一页面携带的navigation进行导航,但是如果方法是在一个纯js文件里面呢??怎么导航???例如我封装了一个axios.js函数,在响应拦截器里面设置当后端返回登录过期就要跳转到登录页面,那这样该怎么实现呢???就以这个案例做一下下面的封装,这个封装的函数可以在纯js文件进行调用跳转,无需依赖于页面的导航
创建工具文件
- 这个工具文件封装了一个导航,可以跳转指定页面,同时也封装了一个函数用于返回几层页面
import {createNavigationContainerRef} from '@react-navigation/native';
import {StackActions} from '@react-navigation/native';
export const navigationRef = createNavigationContainerRef();
/**
* 跳转到某个页面
* @param name 页面name
* @param params 可选,参数携带
*/
export const to = (name, params = {}) => {
if (navigationRef.isReady()) {
// @ts-ignore
navigationRef.navigate(name, params);
}
};
/**
* 返回
* @param count 返回几层,不写返回一层
*/
export const back = (count = 1) => {
if (navigationRef.isReady()) {
navigationRef.dispatch(StackActions.pop(count));
}
};
导航器文件内添加这个工具文件属性
如果根据上面的配置配置了导航器,就找到导航器,在这个导航器页面就两个操作
- 引入工具包,解构出navigationRef
- 将工具包的navigationRef添加到NavigationContainer组件的ref属性上
下面是示例代码
import React, {Component, useContext, useEffect, useState} from 'react';
import {createStackNavigator} from '@react-navigation/stack';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import {NavigationContainer} from '@react-navigation/native';
import LoginScreen from '../pages/user/signin'; // 登录页
import StaffIndex from '../pages/coach/staff/index'; // 数据页面
const {width, height} = Dimensions.get('window');
const Tab = createBottomTabNavigator();
// 1.引入工具包
import {navigationRef} from '../utils/index';
const Stack = createStackNavigator();
export default function Index() {
// 阻止手势左右滑动返回
useEffect(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
() => {
// 在此处添加你的逻辑来阻止安卓上的返回事件
return true; // 返回 true 表示已经处理了返回事件,不会继续传递
},
);
return () => backHandler.remove();
}, []);
return (
// 2.将工具包添加到 NavigationContainer 属性上面去
<NavigationContainer ref={navigationRef}>
<Stack.Navigator
screenOptions={{headerShown: false, gestureEnabled: false}}>
<Stack.Screen name="StaffIndex" component={StaffIndex} />
...
其他组件配置
...
<Stack.Screen name="Privacy" component={Privacy} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({});
使用
这里使用我个人封装的axios文件,当后端返回的是登录过期,则跳转登录页面,这个页面也是两步操作
- 引入工具包,解构出to方法
- 登录过期,使用to方法执行跳转首页
下面是示例代码
import axios from 'axios';
import Toast from 'react-native-root-toast';
import AsyncStorage from '@react-native-async-storage/async-storage';
// 1.引入工具包,解构出to方法
import {to} from '../utils/index';
const instace = axios.create({
baseURL: 'https://internal.takehr.cn', //接口地址
timeout: 5000, //超时时间
});
//配置请求拦截器,在请求之前的数据处理,比如在请求头添加token,所有的请求都会经过拦截器
instace.interceptors.request.use(
async config => {
try {
const token = await AsyncStorage.getItem('token');
const venue = await AsyncStorage.getItem('venueSn');
if (token) {
// console.log('JSON.parse(token)',JSON.parse(token));
config.headers.token = JSON.parse(token);
}
if (venue) {
// console.log('JSON.parse(venue)', JSON.parse(venue));
config.headers['venue-sn'] = JSON.parse(venue);
}
// console.log('config', config);
return config;
} catch (error) {
return Promise.reject(error);
}
},
err => {
return Promise.reject(err);
},
);
//配置响应拦截器
// 响应拦截器:在请求响应之后对数据处理,比如:登录失败、请求数据失败的处理
instace.interceptors.response.use(
response => {
if(response.data.code === '登录过期'){
// 2.登录过期,使用to方法执行跳转首页
to('LoginScreen');
return;
}
if (response.data.code != 1) {
Toast.show(response.data.msg, {
duration: 2000, // 显示时长
position: Toast.positions.CENTER, // 将弹窗内容居中显示
});
}
return response; //这里的response就是请求成功后的res , response.data即是请求成功后回调函数内的参数res.data
},
err => {
console.log('err', err);
Toast.show(err.message, {
duration: 2000, // 显示时长
position: Toast.positions.CENTER, // 将弹窗内容居中显示
});
return Promise.reject(err); //将错误消息挂到promise的失败函数上
},
);
//封装请求的api
const callapi = (method = 'GET', url, data = {}, customHeaders = {}) => {
if (method === 'GET') {
return instace({
method,
url,
params: data,
...customHeaders,
});
} else {
return instace({
method,
url,
params: {},
data: data,
...customHeaders,
});
}
};
//封装GET,POST请求函数
export const getapi = (url, data, customHeaders) =>
callapi('GET', url, data, customHeaders);
export const postapi = (url, data, customHeaders) =>
callapi('POST', url, data, customHeaders);