react-native-scrollable-tab-view是一个非常好用的TabBar组件,支持滑动,可以实现标签超过屏幕宽度的情况。但是有时会需要实现比如提示未读个数、定制样式这些需求,那么已有的功能就不能满足需求了。现在实现一个类似下图可标记未读及数量的自定义TabBar。原图找不到了,就类似网易首页顶部的tabbar,包括显示数字。
![](https://i-blog.csdnimg.cn/blog_migrate/d467e42ca3b6aa221e17afb38031278e.jpeg)
<ScrollableTabView
style={styles.container}
locked={true}
tabBarUnderlineStyle={styles.indicator}
tabBarBackgroundColor='#fff'
tabBarActiveTextColor="#485FF9"
tabBarInactiveTextColor="#000000"
tabBarTextStyle={styles.textSize}
initialPage={0}
renderTabBar={() => <DefaultTabBar />}>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面1</Text>
</View>
</View>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面2</Text>
</View>
</View>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面3</Text>
</View>
</View>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面4</Text>
</View>
</View>
</ScrollableTabView>
属性renderTabBar就是tabbar组件。官方默认给了几个样式,如果实现自定义样式,就自己写一个组件然后赋值给它。
自定义TabBar样式
propTypes: {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
backgroundColor: PropTypes.string,
activeTextColor: PropTypes.string,
inactiveTextColor: PropTypes.string,
scrollOffset: PropTypes.number,
style: ViewPropTypes.style,
tabStyle: ViewPropTypes.style,
tabsContainerStyle: ViewPropTypes.style,
textStyle: Text.propTypes.style,
renderTab: PropTypes.func,
underlineStyle: ViewPropTypes.style,
onScroll: PropTypes.func,
}
ScrollableTabView的变量有上述这些,可以通过this.props.属性名来使用。比如:this.props.activeTab,是当前选择的tab,那么我们就可以利用这个属性来知道当前选择的是哪个tab。
另外:this.props.containerWidth是tabbar的长度,this.props.tabs.length是tabbar的标签个数。通过这两个,可以实现底部那条选中哪个标签移动到哪个标签下的底部线的样式。
还有,底部线移动的动画要怎么实现呢?在源码里看到这段注释:
// Animated.add and Animated.divide do not currently support listeners so
// we have to polyfill it here since a lot of code depends on being able
// to add a listener to `scrollValue`. See https://github.com/facebook/react-native/pull/12620.
也就是说ScrollableTabView通过scrollValue来实现了一个组合动画,就直接使用this.props.scrollValue来使用已经实现的底部线的动画,可以通过动画的插值函数interpolate来实现我们想要的底部线的宽度,或实现移动时的伸缩效果,都可以通过这个来实现。
直接上自定义的tabbar
/**
* @desc ScrollableTabView的自定义tabBar组件,赋值给renderTabBar
* 属性tabLabelNames,来记录需要的属性:showNum 是否显示数字,num 数量,showIcon 是否只展示小圆点。
* @author MaRui
*/
import React from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Animated,
} from 'react-native';
class CustomTabBar extends React.Component {
constructor(props) {
super(props);
}
render() {
const containerWidth = this.props.containerWidth;
const numberOfTabs = this.props.tabs.length;
const tabUnderlineStyle = {
position: 'absolute',
width: containerWidth / numberOfTabs,
height: 4,
backgroundColor: 'navy',
bottom: 0,
justifyContent: 'center'
};
const translateX = this.props.scrollValue.interpolate({
inputRange: [0, 1],
outputRange: [0, containerWidth / numberOfTabs],
});
return <View style={[styles.tabs, this.props.style, {backgroundColor: this.props.backgroundColor,}]}>
{this.props.tabLabelNames.map((tab, page) => {
return <TouchableOpacity key={tab.name} onPress={() => this.props.goToPage(page)} style={styles.tab}>
<Text
style={[styles.tabText, page === this.props.activeTab ? {color: this.props.activeTextColor} : {color: this.props.inactiveTextColor}, this.props.textStyle]}>{tab.name}</Text>
<View
style={[
(tab.showNum && tab.num !== 0) ? styles.tabNum : (tab.showIcon ? styles.tabDot : null),
((tab.showNum && tab.num !== 0) || tab.showIcon) ? styles.show : styles.hide]}>
<Text
style={styles.tabNumText}>{(tab.showNum && tab.num !== 0) ? (tab.num > 99 ? '99+' : tab.num) : null}</Text>
</View>
</TouchableOpacity>;
})}
<Animated.View
style={[
tabUnderlineStyle,
{
transform: [
{translateX},
]
},
this.props.underlineStyle,
]}
/>
</View>;
}
}
const styles = StyleSheet.create({
tab: {
flexDirection: 'row',
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
tabs: {
height: 45,
flexDirection: 'row',
justifyContent: 'space-around'
},
tabText: {
fontSize: 14
},
tabNum: {
borderRadius: 10,
backgroundColor: '#E72E2E',
height: 20,
minWidth: 20,
paddingHorizontal: 2,
marginLeft: 2,
alignItems: 'center',
justifyContent: 'center'
},
tabNumText: {
color: 'white',
fontSize: 10,
textAlign: 'center',
lineHeight: 20,
minWidth: 20
},
tabDot: {
borderRadius: 8,
marginLeft: 2,
height: 8,
width: 8,
backgroundColor: '#E72E2E'
},
show: {
display: 'flex'
},
hide: {
display: 'none'
}
});
export default MineTabBar;
例子
<ScrollableTabView
style={styles.container}
locked={true}
tabBarUnderlineStyle={styles.indicator}
tabBarBackgroundColor='#fff'
tabBarActiveTextColor="#485FF9"
tabBarInactiveTextColor="#000000"
tabBarTextStyle={styles.textSize}
initialPage={0}
renderTabBar={() => <CustomTabBar tabLabelNames={[
{name: '标签1', showNum: true, showIcon: false, num: this.state.num1},
{name: '标签2', showNum: false, showIcon: this.props.hasUnread},
{name: '标签3', showNum: false, showIcon: false},
{name: '标签4', showNum: false, showIcon: false}]}/>}>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面1</Text>
</View>
</View>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面2</Text>
</View>
</View>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面3</Text>
</View>
</View>
<View style={{flex: 1}}>
<View style={{flex:1 ,width:'100%'}}>
<Text>界面4</Text>
</View>
</View>
</ScrollableTabView>