首先看下效果
react-navigation是不支持自定义背景的,但产品有需求没办法。只能扩展喽~~ 注意那棵小树的处理,高度是透出来的,并且整个背景是在page之上的!只有后面两个按钮可在页面中进行单击,滑动跳转。单击游戏,则退出此业务模块。
修改如下:
1.node_modules/react-navigation/src/views/TabView/TabView.js
在外面将背景图和style传参进来,在_renderTabBar方法中实现渲染背景图,并根据传参是否支持背景图,红色部分为主要修改位置, 请注意_renderTabBar()方法的修改
/* @flow */
import * as React from 'react';
import {
Image,
Text,
} from 'react-native';
import {View, StyleSheet, Platform} from 'react-native';
import {TabViewAnimated, TabViewPagerPan} from 'react-native-tab-view';
import type {Layout} from 'react-native-tab-view/src/TabViewTypeDefinitions';
import SceneView from '../SceneView';
import withCachedChildNavigation from '../../withCachedChildNavigation';
import SafeAreaView from '../SafeAreaView';
import type {
NavigationScreenProp,
NavigationRoute,
NavigationState,
NavigationRouter,
NavigationTabScreenOptions,
} from '../../TypeDefinition';
export
type
TabViewConfig = {
tabBarComponent? : React.ComponentType < * >,
tabBarPosition? : 'top' | 'bottom',
tabBarOptions? : {},
swipeEnabled? : boolean,
animationEnabled? : boolean,
configureTransition? : (currentTransitionProps: Object,
nextTransitionProps: Object) => Object,
lazy? : boolean,
initialLayout? : Layout,
};
export
type
TabScene = {
route: NavigationRoute,
focused: boolean,
index: number,
tintColor? : ? string,
};
type
Props = {
tabBarComponent? : React.ComponentType < * >,
tabBarPosition? : 'top' | 'bottom',
tabBarOptions? : {},
swipeEnabled? : boolean,
animationEnabled? : boolean,
configureTransition? : (currentTransitionProps: Object,
nextTransitionProps: Object) => Object,
lazy? : boolean,
initialLayout: Layout,
screenProps? : {},
navigation: NavigationScreenProp < NavigationState >,
router: NavigationRouter < NavigationState, NavigationTabScreenOptions >,
childNavigationProps
:
{
[key
:
string
]:
NavigationScreenProp < NavigationRoute >,
}
,
}
;
class TabView extends React.PureComponent<Props> {
static defaultProps = {
// fix for https://github.com/react-native-community/react-native-tab-view/issues/312
initialLayout: Platform.select({
android: {width: 1, height: 0},
}),
};
_handlePageChanged = (index: number) => {
const {navigation, onPageChange} = this.props;
onPageChange(index, this.props.screenProps);
navigation.navigate(navigation.state.routes[index].routeName);
};
_renderScene = ({route}: any) => {
const {screenProps} = this.props;
const childNavigation = this.props.childNavigationProps[route.key];
const TabComponent = this.props.router.getComponentForRouteName(
route.routeName
);
return (
<View style={styles.page}>
<SceneView
screenProps={screenProps}
component={TabComponent}
navigation={childNavigation}
/>
</View>
);
};
_getLabel = ({route, tintColor, focused}: TabScene) => {
const options = this.props.router.getScreenOptions(
this.props.childNavigationProps[route.key],
this.props.screenProps || {}
);
if (options.tabBarLabel) {
return typeof options.tabBarLabel === 'function'
? options.tabBarLabel({tintColor, focused})
: options.tabBarLabel;
}
if (typeof options.title === 'string') {
return options.title;
}
return route.routeName;
};
_getOnPress = (previousScene: TabScene, {route}: TabScene) => {
const options = this.props.router.getScreenOptions(
this.props.childNavigationProps[route.key],
this.props.screenProps || {}
);
return options.tabBarOnPress;
};
_getTestIDProps = ({route}: TabScene) => {
const options = this.props.router.getScreenOptions(
this.props.childNavigationProps[route.key],
this.props.screenProps || {}
);
return options.tabBarTestIDProps;
};
_renderIcon = ({focused, route, tintColor}: TabScene) => {
const options = this.props.router.getScreenOptions(
this.props.childNavigationProps[route.key],
this.props.screenProps || {}
);
if (options.tabBarIcon) {
return typeof options.tabBarIcon === 'function'
? options.tabBarIcon({tintColor, focused})
: options.tabBarIcon;
}
return null;
};
_renderTabBar = (props: *) => {
const {
tabBarOptions,
tabBarComponent: TabBarComponent,
animationEnabled,
} = this.props;
if (typeof TabBarComponent === 'undefined') {
return null;
}
const bgImgOptions = tabBarOptions.bgImgOptions;
const marginTopValue = bgImgOptions && bgImgOptions.showBgImg ? bgImgOptions.marginTop : 0;
return (
<View style={{marginTop: marginTopValue}}>
{this._renderBgImg(bgImgOptions)}
<TabBarComponent
{...props}
{...tabBarOptions}
screenProps={this.props.screenProps}
navigation={this.props.navigation}
getLabel={this._getLabel}
getTestIDProps={this._getTestIDProps}
getOnPress={this._getOnPress}
renderIcon={this._renderIcon}
animationEnabled={animationEnabled}
/>
</View>
);
};
_renderBgImg(bgImgOptions) {
if (bgImgOptions && bgImgOptions.showBgImg) {
const widthValue = bgImgOptions.width;
const heightValue = bgImgOptions.height;
return (
<View>
<Image style={{width: widthValue, height: heightValue, position: 'absolute'}}
source={bgImgOptions.bgImg} resizeMode="cover"/>
</View>
);
}
}
_renderPager = (props: *) => <TabViewPagerPan {...props} />;
render() {
const {
router,
tabBarComponent,
tabBarPosition,
animationEnabled,
configureTransition,
swipeEnabled,
lazy,
initialLayout,
screenProps,
} = this.props;
let renderHeader;
let renderFooter;
let renderPager;
const {state} = this.props.navigation;
const options = router.getScreenOptions(
this.props.childNavigationProps[state.routes[state.index].key],
screenProps || {}
);
const tabBarVisible =
options.tabBarVisible == null ? true : options.tabBarVisible;
if (tabBarComponent !== undefined && tabBarVisible) {
if (tabBarPosition === 'bottom') {
renderFooter = this._renderTabBar;
} else {
renderHeader = this._renderTabBar;
}
}
if (
(animationEnabled === false && swipeEnabled === false) ||
typeof configureTransition === 'function'
) {
renderPager = this._renderPager;
}
const props = {
lazy,
initialLayout,
animationEnabled,
configureTransition,
swipeEnabled,
renderPager,
renderHeader,
renderFooter,
renderScene: this._renderScene,
onIndexChange: this._handlePageChanged,
navigationState: this.props.navigation.state,
screenProps: this.props.screenProps,
style: styles.container,
};
// $FlowFixMe: mismatch with react-native-tab-view type
return <TabViewAnimated {...props} />;
}
}
export default withCachedChildNavigation(TabView);
const styles = StyleSheet.create({
container: {
flex: 1,
},
page: {
flex: 1,
overflow: 'hidden',
backgroundColor: 'transparent',
},
});
2.如果有需求,第一个按钮直接返回到主界面,相对来说,当前模块只是一个子业务模块的时候,我们可以改变下面TabBottom的布局,android和iOS的平台不一致,如有需要只改变:
npde_modules/react-native-tab-view/TabBar.js 插入红色的部分,此为render方法中return实现// android平台下面的tabBar的实现
return (
<Animated.View style={[styles.tabBar, this.props.style]}>
<Animated.View
pointerEvents="none"
style={[
styles.indicatorContainer,
scrollEnabled
? { width: tabBarWidth, transform: [{ translateX }] }
: null,
]}
>
{this._renderIndicator({
...this.props,
width: new Animated.Value(finalTabWidth),
})}
</Animated.View>
<View style={styles.scroll}>
<ScrollView
horizontal
keyboardShouldPersistTaps="always"
scrollEnabled={scrollEnabled}
bounces={false}
alwaysBounceHorizontal={false}
scrollsToTop={false}
showsHorizontalScrollIndicator={false}
automaticallyAdjustContentInsets={false}
overScrollMode="never"
contentContainerStyle={[
styles.tabContent,
scrollEnabled ? null : styles.container,
]}
scrollEventThrottle={16}
onScroll={this._handleScroll}
onScrollBeginDrag={this._handleBeginDrag}
onScrollEndDrag={this._handleEndDrag}
onMomentumScrollBegin={this._handleMomentumScrollBegin}
onMomentumScrollEnd={this._handleMomentumScrollEnd}
contentOffset={this.state.initialOffset}
ref={this._setRef}
>
{this.props.gameButton}
{routes.map((route, i) => {
const focused = index === i;
const outputRange = inputRange.map(
inputIndex => (inputIndex === i ? 1 : 0.7)
);
const opacity = Animated.multiply(
this.state.visibility,
position.interpolate({
inputRange,
outputRange,
})
);
const scene = {
route,
focused,
index: i,
};
const label = this._renderLabel(scene);
const icon = this.props.renderIcon
? this.props.renderIcon(scene)
: null;
const badge = this.props.renderBadge
? this.props.renderBadge(scene)
: null;
const tabStyle = {};
tabStyle.opacity = opacity;
if (icon) {
if (label) {
tabStyle.paddingTop = 8;
} else {
tabStyle.padding = 12;
}
}
const passedTabStyle = StyleSheet.flatten(this.props.tabStyle);
const isWidthSet =
(passedTabStyle &&
typeof passedTabStyle.width !== 'undefined') ||
scrollEnabled === true;
const tabContainerStyle = {};
if (isWidthSet) {
tabStyle.width = finalTabWidth;
}
if (passedTabStyle && typeof passedTabStyle.flex === 'number') {
tabContainerStyle.flex = passedTabStyle.flex;
} else if (!isWidthSet) {
tabContainerStyle.flex = 1;
}
const accessibilityLabel =
route.accessibilityLabel || route.title;
return (
<TouchableItem
borderless
key={route.key}
testID={route.testID}
accessible={route.accessible}
accessibilityLabel={accessibilityLabel}
accessibilityTraits="button"
pressColor={this.props.pressColor}
pressOpacity={this.props.pressOpacity}
delayPressIn={0}
onPress={() => {
// eslint-disable-line react/jsx-no-bind
const { onTabPress, jumpToIndex } = this.props;
jumpToIndex(i);
if (onTabPress) {
onTabPress(scene);
}
}}
style={tabContainerStyle}
>
<View pointerEvents="none" style={styles.container}>
<Animated.View
style={[
styles.tabItem,
tabStyle,
passedTabStyle,
styles.container,
]}
>
{icon}
{label}
</Animated.View>
{badge ? (
<Animated.View
style={[
styles.badge,
{ opacity: this.state.visibility },
]}
>
{badge}
</Animated.View>
) : null}
</View>
</TouchableItem>
);
})}
</ScrollView>
</View>
</Animated.View>
);
在node_modules/react-navigation/src/views/TabView/TabBarBottom.js下插入红色部分,代码为render方法中的的return部分// 针对iOS
return (
<Animated.View style={animateStyle}>
<SafeAreaView
style={tabBarStyle}
forceInset={{ bottom: 'always', top: 'never' }}
>
{this.props.gameButton}
{routes.map((route: NavigationRoute, index: number) => {
const focused = index === navigation.state.index;
const scene = { route, index, focused };
const onPress = getOnPress(previousScene, scene);
const outputRange = inputRange.map(
(inputIndex: number) =>
inputIndex === index
? activeBackgroundColor
: inactiveBackgroundColor
);
const backgroundColor = position.interpolate({
inputRange,
outputRange: (outputRange: Array<string>),
});
const justifyContent = this.props.showIcon ? 'flex-end' : 'center';
const extraProps = this._renderTestIDProps(scene) || {};
const { testID, accessibilityLabel } = extraProps;
return (
<TouchableWithoutFeedback
key={route.key}
testID={testID}
accessibilityLabel={accessibilityLabel}
onPress={() =>
onPress
? onPress({ previousScene, scene, jumpToIndex })
: jumpToIndex(index)}
>
<Animated.View
style={[
styles.tab,
isLandscape && useHorizontalTabs && styles.tabLandscape,
!isLandscape && useHorizontalTabs && styles.tabPortrait,
{ backgroundColor },
tabStyle,
]}
>
{this._renderIcon(scene)}
{this._renderLabel(scene)}
</Animated.View>
</TouchableWithoutFeedback>
);
})}
</SafeAreaView>
</Animated.View>
);
3.使用的地方tabBarOptions里增加参数bgImgOptions
//设置Tab标签的属性
tabBarOptions: {
upperCaseLabel: false,
showIcon: true,//是否显示图标,默认关闭
showLabel: true,//是否显示label,默认开启
activeTintColor: 'white',//label和icon的前景色 活跃状态下(选中)
inactiveTintColor: '#FCFCFC',//label和icon的前景色 活跃状态下(未选中)
allowFontScaling: false,
style: Platform.OS === 'ios' ? iosTabBarStyle : androidTabBarStyle, //TabNavigator 的背景颜色
indicatorStyle: {//标签指示器的样式对象(选项卡底部的行)。安卓底部会多出一条线,可以将height设置为0来暂时解决这个问题
height: 0,
},
labelStyle: {//文字的样式
fontSize: 13,
marginTop: -5,
marginBottom: 5,
},
iconStyle: {//图标的样式
width: 48,
height:48,
},
bgImgOptions: {
showBgImg: "true",
width: ScreenWidth,
height: 80,
marginTop: -30,
bgImg: require('../img/tabicon/tab_bg.png'), // 此为TabBar添加背景图
gameButton:<Text style={{width:100}}>退出游戏</Text> // 此为在TabBar中增加额外的View元素进行退出子业务模块操作
}
感谢大家的关注!