不知道大家有没有遇到这样尴尬的问题:使用react-navigation createBottomTabNavigator实现底部tabBar,在tabBar的一个界面上弹出一个抽屉,发现抽屉没有覆盖tabBar?或者实现了抽屉覆盖tabBar,结果在tabBar界面上不能跳转到新的界面?
一、问题描述
大致需求如下:用户打开app底部有tabBar,tabBar有三个界面:Home,Release,Order;在Home界面左上角有一个按钮A,用户点击按钮A打开抽屉,显示界面Mine,点击空白处关闭抽屉;home界面有另一个按钮B,点击按钮B跳转新界面Login。
二、实现思路
来说说我的实现思路:
- react-navigation createBottomTabNavigator实现底部tabBar;
- createStackNavigator 中直接添加TabBar为Main;
- react-native-drawer实现Drawer,弹出Mine;
- this.props.navigation.push('Login')实现Home跳转Login界面;
Home.js
import React, {PureComponent} from 'react';
import {connect} from 'react-redux';
import {
View,
DeviceEventEmitter,
Dimensions,
StyleSheet,
} from 'react-native';
import * as Color from '../../constants/colors';
import Text from '../../components/common/scalingText';
import NavigationBar from '../../components/navigationBar/navigationBar';
import styles from '../../../assets/css/home';
import ControlPanel from '../../containers/mine/mine';
import Drawer from 'react-native-drawer'
const { width, height } = Dimensions.get('window');
class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
DeviceEventEmitter.addListener('clickDrawer',(events) =>{
if(this._drawer && this._drawer.open()){
this.closeControlPanel();
}else{
this.openControlPanel();
}
});
}
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
render() {
return (
<Drawer
ref={(ref) => this._drawer = ref}
type="displace"
content={<ControlPanel navigation={this.props.navigation}/>}
tapToClose={true}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity:(2-ratio)/2 }
})}
>
<NavigationBar
title={'首页'}
router={this.props.navigation}
hiddenBackIcon={false}
backIconFont=''
backViewClick={()=>{
DeviceEventEmitter.emit('clickDrawer');
console.log('this.props.navigation',this.props.navigation);
}}
/>
<Text onPress={()=>{
this.props.navigation.push('PhoneCodeLogin');
}}
>登录</Text>
</Drawer>
);
}
}
const drawerStyles = {
drawer: { shadowColor: '#000000', shadowOpacity: 0.8, shadowRadius: 3},
main: {paddingLeft: 3},
}
function mapStateToProps(state){
return {
nav: state.nav
};
}
function mapDispatchToProps (dispatch){
return {};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
router.js
...
import TabBar from './tabBar';
const StackRouteConfigs = {
Login: {
screen: Login,
navigationOptions: {
header: null
}
},
Main: {
screen: TabBar,
navigationOptions: {
header: null
}
},
}
...
上图效果图:
发现问题:这种实现思路,Home可以正常跳转Login界面,但是Drawer弹出Mine并没有将TabBar覆盖。
改进1
- react-navigation createBottomTabNavigator实现底部tabBar;
- 新建Main文件,将TabBar包在其中,createStackNavigator 中添加Main为Main;
- react-native-drawer实现Drawer,弹出Mine;
- this.props.navigation.push('Login')实现Home跳转Login界面;
Main.js
import React, {PureComponent} from 'react';
import {connect} from 'react-redux';
import {
View,
Text,
StyleSheet,
DeviceEventEmitter
} from 'react-native';
import TabBar from '../../routes/tabBar';
import Drawer from 'react-native-drawer'
import ControlPanel from '../../containers/mine/mine';
class Main extends PureComponent {
constructor(props) {
super(props);
}
componentDidMount() {
DeviceEventEmitter.addListener('clickDrawer',(events) =>{
if(this._drawer && this._drawer.open()){
this.closeControlPanel();
}else{
this.openControlPanel();
}
});
}
closeControlPanel = () => {
this._drawer.close()
};
openControlPanel = () => {
this._drawer.open()
};
render() {
return (
<Drawer
ref={(ref) => this._drawer = ref}
type="displace"
content={<ControlPanel navigation={this.props.navigation}/>}
tapToClose={true}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={(ratio) => ({
main: { opacity:(2-ratio)/2 }
})}
>
<View
style={styles.container}
>
<TabBar/>
</View>
</Drawer>
);
}
}
const drawerStyles = {
drawer: { shadowColor: '#000000', shadowOpacity: 0.8, shadowRadius: 3},
main: {paddingLeft: 3},
}
const styles =StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5'
},
});
function mapStateToProps(state){
return {};
}
function mapDispatchToProps (dispatch){
return {};
}
export default connect(mapStateToProps, mapDispatchToProps)(Main);
router.js
...
import Main from '../containers/app/main';
const StackRouteConfigs = {
Login: {
screen: Login,
navigationOptions: {
header: null
}
},
Main: {
screen: Main,
navigationOptions: {
header: null
}
},
}
...
上图效果图:
发现问题:这种实现思路,Drawer弹出Mine覆盖了TabBar,但Home不可以跳转Login界面,报错。
原因:通过打印this.props.navigation发现根本没有push方法,通过main文件包装tabBar导致其不是一个Stack,不具有Stack中push、pop等方法。
改进2
- react-navigation createBottomTabNavigator实现底部tabBar;
- react-navigation createDrawerNavigator实现Drawer;
- 新建drawer文件,将Drawer作为TabBar的父导航,添加到createStackNavigator中;
- this.props.navigation.push('Login')实现Home跳转Login界面;
点击Home界面按钮A打开抽屉方法:
this.props.navigation.toggleDrawer(); // 如果抽屉状态为打开,操作为关闭;如果抽屉状态为关闭,操作为打开
this.props.navigation.openDrawer(); // 打开抽屉
this.props.navigation.closeDrawer(); // 关闭抽屉
最后发现,改进之后的可以满足需求,下面直接上代码~
三、具体代码
root.js
import React from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from '../app.json';
import {Provider} from 'react-redux';
import configureStore from './store/store';
import {AppNavigator} from './routes/routers';
const store = configureStore();
export default class Root extends React.Component{
render(){
return(
<Provider store={store}>
<AppNavigator/>
</Provider>
)
}
}
AppRegistry.registerComponent(appName, () => Root);
router.js
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {
createStackNavigator,
} from 'react-navigation';
import {
reduxifyNavigator,
createReactNavigationReduxMiddleware,
} from 'react-navigation-redux-helpers';
import Login from '../containers/login/login';
import Drawer from './drawer';
const navMiddleware = createReactNavigationReduxMiddleware(
'root',
state => state.nav
);
const StackNavigatorConfigs = {
initialRouteName: 'Main', // 初始化哪个界面为根界面
mode: 'card', // 跳转方式:默认的card,在iOS上是从右到左跳转,在Android上是从下到上,都是使用原生系统的默认跳转方式。
headerMode:'screen', // 导航条动画效果:float表示会渐变,类似于iOS的原生效果,screen表示没有渐变。none表示隐藏导航条
navigationOptions: {
gesturesEnabled: false, //是否可以使用手势关闭此屏幕。在iOS上默认为true,在Android上为false
},
};
const StackRouteConfigs = {
Login:{
screen: Login,
navigationOptions: {
header: null
}
},
Main: {
screen: Drawer,
navigationOptions: {
header: null
}
},
};
const RootNavigator = createStackNavigator(StackRouteConfigs, StackNavigatorConfigs);
const AppWithNavigationState = reduxifyNavigator(RootNavigator, 'root');
const mapStateToProps = state => ({
state: state.nav,
});
const AppNavigator = connect(mapStateToProps)(AppWithNavigationState);
export {RootNavigator, AppNavigator, navMiddleware};
drawer.js
import React, {Component} from 'react';
import {
Dimensions,
} from 'react-native';
import {
createDrawerNavigator,
} from "react-navigation";
import Mine from '../containers/mine/mine';
import TabBar from './tabBar';
const {width,height} = Dimensions.get('window');
export default Drawer = createDrawerNavigator({
TabBar: {
screen: TabBar,
navigationOptions: {
header: null
}
},
},
{
drawerPosition:'left',
drawerWidth: width * 0.8,
drawerLockMode:'locked-closed',
useNativeAnimations:true,
contentComponent: props => {
return <Mine {...props}/>;
}
})
tabBar.js
import React, {Component} from 'react';
import {
StyleSheet,
Dimensions,
Platform,
Text,
TouchableOpacity,
Image,
View
} from 'react-native';
import {
createBottomTabNavigator,
} from "react-navigation";
import TabBarIcon from '../../assets/imgs/tabBar/tabbar.png';
import Home from '../containers/home/home';
import Release from '../containers/release/release';
import Order from '../containers/order/order';
import * as Color from '../constants/colors';
const {width,height} = Dimensions.get('window');
const styles = StyleSheet.create({
pressedIcon: {
fontFamily: 'iconfont',
fontSize: 23,
color: Color.BLUE_17A9DF,
marginTop: 7,
},
renderIcon: {
fontFamily: 'iconfont',
fontSize: 23,
color: Color.GRAY_B4B4B4,
marginTop: 7,
}
});
export default Tab = createBottomTabNavigator({
Home: {
screen: Home,
navigationOptions: {
tabBarPosition: 'bottom',
tabBarLabel: '首页',
showLabel: false,
tabBarIcon: ({tintColor, focused}) => (
focused ? <Text style={styles.pressedIcon}></Text> : <Text style={styles.renderIcon}></Text>
),
}
},
Release: {
screen: Release,
navigationOptions: {
tabBarVisible: false, // tabBar导航栏显示/隐藏 false:隐藏 true:显示
tabBarPosition: 'bottom',
tabBarLabel: '发布',
showLabel: false,
tabBarIcon: ({tintColor, focused}) => (
<Image source={TabBarIcon} style={{marginBottom:25}}></Image>
),
}
},
Order: {
screen: Order,
navigationOptions: {
tabBarPosition: 'bottom',
tabBarLabel: '订单',
showLabel: false,
tabBarIcon: ({tintColor, focused}) => (
focused ? <Text style={styles.pressedIcon}></Text> : <Text style={styles.renderIcon}></Text>
),
}
},
}, {
animationEnabled: true,
swipeEnabled: false,
//是否可以滑动切换
swipeEnabled: false,
//切换是否有动画
animationEnabled: true,
//进入App的首页面
initialRouteName: 'Home',
//对于导航的设置
tabBarOptions: {
activeTintColor: Color.BLUE_17A9DF,
inactiveTintColor: Color.GRAY_B4B4B4,
//android特有下划线的颜色1
indicatorStyle: {height: 0},
//文字的样式
labelStyle: {
fontSize: 10,
paddingBottom: 4
},
//对于导航的stytles
style :{
borderTopColor: 'rgba(0,0,0,0.2)',
borderTopWidth: 1,
backgroundColor: Color.WHITE_FFFFFF,
// height: Dimensions.get('window').height*0.08,
}
}
});
Home.js
import React, {PureComponent} from 'react';
import {connect} from 'react-redux';
import {
View,
Dimensions,
} from 'react-native';
import * as Color from '../../constants/colors';
import Text from '../../components/common/scalingText';
import NavigationBar from '../../components/navigationBar/navigationBar';
import styles from '../../../assets/css/home';
const { width, height } = Dimensions.get('window');
class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
}
}
componentDidMount() {
}
render() {
return (
<View style={styles.container}>
<NavigationBar
title={'首页'}
router={this.props.navigation}
hiddenBackIcon={false}
backIconFont=''
backViewClick={()=>{
this.props.navigation.toggleDrawer();
}}
/>
<Text
onPress={()=>{
this.props.navigation.push('Login');
}
>登录</Text>
</View>
);
}
}
function mapStateToProps(state){
return {
nav: state.nav
};
}
function mapDispatchToProps (dispatch){
return {};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
四、效果图
完毕~~~你学会了吗?