Open Jemair opened 6 years ago
自定义defaultTabBar如下:
import {
default as RN,
Animated,
Dimensions,
Platform,
ScrollView,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { TabBarPropsType } from 'rmc-tabs/lib/PropsType';
import { Models } from 'rmc-tabs/lib/Models';
import defaultStyles from 'rmc-tabs/lib/Styles.native';
import React from 'react';
const WINDOW_WIDTH = Dimensions.get('window').width;
export interface PropsType extends TabBarPropsType {
scrollValue?: any;
styles?: typeof defaultStyles;
tabStyle?: RN.ViewStyle;
tabsContainerStyle?: RN.ViewStyle;
/** default: false */
dynamicTabUnderlineWidth?: boolean;
keyboardShouldPersistTaps?: boolean;
tabBarUnderLineWidth:number;
}
export interface StateType {
_leftTabUnderline: Animated.Value;
_widthTabUnderline: Animated.Value;
_containerWidth: number;
_tabContainerWidth: number;
}
export class DefaultTabBar extends React.PureComponent<PropsType, StateType> {
static defaultProps = {
animated: true,
tabs: [],
goToTab: () => { },
activeTab: 0,
page: 5,
tabBarUnderlineStyle: {},
tabBarBackgroundColor: '#fff',
tabBarActiveTextColor: '',
tabBarInactiveTextColor: '',
tabBarTextStyle: {},
dynamicTabUnderlineWidth: false,
styles: defaultStyles,
tabBarUnderLineWidth:50,
} as PropsType;
_tabsMeasurements: any[] = [];
_tabContainerMeasurements: any;
_containerMeasurements: any;
_scrollView: any;
constructor(props: PropsType) {
super(props);
this.state = {
_leftTabUnderline: new Animated.Value(0),
_widthTabUnderline: new Animated.Value(0),
_containerWidth: this.props.tabBarUnderLineWidth,
_tabContainerWidth: WINDOW_WIDTH,
};
}
componentDidMount() {
this.props.scrollValue && this.props.scrollValue.addListener(this.updateView);
}
updateView = (offset: any) => {
const position = Math.floor(offset.value);
const pageOffset = offset.value % 1;
const tabCount = this.props.tabs.length;
const lastTabPosition = tabCount - 1;
if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) {
return;
}
if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) {
this.updateTabPanel(position, pageOffset);
this.updateTabUnderline(position, pageOffset, tabCount);
}
}
necessarilyMeasurementsCompleted(position: number, isLastTab: boolean) {
return this._tabsMeasurements[position] &&
(isLastTab || this._tabsMeasurements[position + 1]) &&
this._tabContainerMeasurements &&
this._containerMeasurements;
}
updateTabPanel(position: number, pageOffset: number) {
const containerWidth = this._containerMeasurements.width;
const tabWidth = this._tabsMeasurements[position].width;
const nextTabMeasurements = this._tabsMeasurements[position + 1];
const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0;
const tabOffset = this._tabsMeasurements[position].left;
const absolutePageOffset = pageOffset * tabWidth;
let newScrollX = tabOffset + absolutePageOffset;
newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2;
newScrollX = newScrollX >= 0 ? newScrollX : 0;
if (Platform.OS === 'android') {
this._scrollView.scrollTo({ x: newScrollX, y: 0, animated: false, });
} else {
const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width);
newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX;
this._scrollView.scrollTo({ x: newScrollX, y: 0, animated: false, });
}
}
updateTabUnderline(position: number, pageOffset: number, tabCount: number) {
const { dynamicTabUnderlineWidth } = this.props;
if (0 <= position && position <= tabCount - 1) {
if (dynamicTabUnderlineWidth) {
const nowLeft = this._tabsMeasurements[position].left;
const nowRight = this._tabsMeasurements[position].right;
const nextTabLeft = this._tabsMeasurements[position + 1].left;
const nextTabRight = this._tabsMeasurements[position + 1].right;
const newLineLeft = pageOffset * nextTabLeft + (1 - pageOffset) * nowLeft;
const newLineRight = pageOffset * nextTabRight + (1 - pageOffset) * nowRight;
this.state._leftTabUnderline.setValue(newLineLeft);
this.state._widthTabUnderline.setValue(newLineRight - newLineLeft);
} else {
const nowLeft = position * this.state._tabContainerWidth / tabCount;
const nextTabLeft = (position + 1) * this.state._tabContainerWidth / tabCount;
const newLineLeft = pageOffset * nextTabLeft + (1 - pageOffset) * nowLeft;
this.state._leftTabUnderline.setValue(newLineLeft);
}
}
}
onPress = (index: number) => {
const { goToTab, onTabClick, tabs } = this.props;
onTabClick && onTabClick(tabs[index], index);
goToTab && goToTab(index);
}
renderTab(tab: Models.TabData, index: number, width: number, onLayoutHandler: any) {
const {
tabBarActiveTextColor: activeTextColor,
tabBarInactiveTextColor: inactiveTextColor,
tabBarTextStyle: textStyle,
activeTab, renderTab,
styles = defaultStyles
} = this.props;
const isTabActive = activeTab === index;
const textColor = isTabActive ?
(activeTextColor || styles.TabBar.activeTextColor) :
(inactiveTextColor || styles.TabBar.inactiveTextColor);
return <TouchableOpacity
activeOpacity={1}
key={`${tab.title}_${index}`}
accessible={true}
accessibilityTraits="button"
onPress={() => this.onPress(index)}
onLayout={onLayoutHandler}
>
<View style={{
...styles.TabBar.tab,
...this.props.tabStyle,
width,
}}>
{
renderTab ? renderTab(tab) :
<Text style={{
color: textColor,
...styles.TabBar.textStyle,
...textStyle
}}>
{tab.title}
</Text>
}
</View>
</TouchableOpacity>;
}
measureTab = (page: number, event: any) => {
const { x, width, height, } = event.nativeEvent.layout;
this._tabsMeasurements[page] = { left: x, right: x + width, width, height };
this.updateView({ value: this.props.scrollValue._value });
}
render() {
const {
tabs, page = 1,
tabBarUnderlineStyle,
tabBarBackgroundColor,
styles = defaultStyles,
tabsContainerStyle,
renderUnderline,
keyboardShouldPersistTaps,
} = this.props;
const tabUnderlineStyle = {
position: 'absolute',
bottom: 0,
...styles.TabBar.underline,
...tabBarUnderlineStyle,
};
const tabWidth = this.state._containerWidth / Math.min(page, tabs.length);
const dynamicTabUnderline = {
left: this.state._leftTabUnderline,
width: this.state._widthTabUnderline,
marginLeft:(tabWidth - this.props.tabBarUnderLineWidth)/2
};
const underlineProps = {
style: {
...tabUnderlineStyle,
...dynamicTabUnderline,
}
};
return <View
style={{
...styles.TabBar.container,
backgroundColor: tabBarBackgroundColor,
}}
onLayout={this.onContainerLayout}
>
<ScrollView
ref={(scrollView: any) => { this._scrollView = scrollView; }}
horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
directionalLockEnabled={true}
bounces={false}
scrollsToTop={false}
scrollEnabled={tabs.length > page}
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
>
<View
style={{
...styles.TabBar.tabs,
...tabsContainerStyle,
backgroundColor: tabBarBackgroundColor,
}}
onLayout={this.onTabContainerLayout}
>
{
tabs.map((name, index) => {
let tab = { title: name } as Models.TabData;
if (tabs.length - 1 >= index) {
tab = tabs[index];
}
return this.renderTab(tab, index, tabWidth, this.measureTab.bind(this, index));
})
}
{
renderUnderline ? renderUnderline(underlineProps) :
<Animated.View {...underlineProps} />
}
</View>
</ScrollView>
</View>;
}
onTabContainerLayout = (e: RN.LayoutChangeEvent) => {
this._tabContainerMeasurements = e.nativeEvent.layout;
let width = this._tabContainerMeasurements.width;
if (width < WINDOW_WIDTH) {
width = WINDOW_WIDTH;
}
this.setState({ _tabContainerWidth: width, });
if (!this.props.dynamicTabUnderlineWidth) {
this.state._widthTabUnderline.setValue(this.props.tabBarUnderLineWidth);
}
this.updateView({ value: this.props.scrollValue._value, });
}
onContainerLayout = (e: RN.LayoutChangeEvent) => {
this._containerMeasurements = e.nativeEvent.layout;
this.setState({ _containerWidth: this._containerMeasurements.width, });
this.updateView({ value: this.props.scrollValue._value, });
}
}
引用如下:
<Tabs
renderTabBar={(props) => <BaseTabBar {...props} tabBarUnderLineWidth={this.props.tabBarUnderLineWidth?this.props.tabBarUnderLineWidth:54}/>}
></Tabs>
今天在开发的时候遇到这样一个问题
设计稿中tabBarItem每个长度为页面的1/6 当不足6个时不自动填满宽度而是将右侧留白 效果如下
但是在defaultTabBar中 是使用js动态添加行内样式到组件上 导致标签样式难以覆盖 同时使用!important强行覆盖之后 由于underline宽度和位置也是写死的 导致underline长度和位置都无法与tab标签对齐
期望增加tabWidth属性 可以自定义设置tab宽度 同时使用offsetLeft动态计算underline位置 可以增强组件的扩展性 自定义tabBar代码如下 (当tab为垂直时使用offsetTop)