react-native-scrollable-tab-view
2019-04-14 本文已影响0人
MasterPaul
效果
QQ20190414-212423-HD.gif
30211555243190_.pic_hd.jpg
import React,{Component} from 'react'
const { ViewPropTypes } = ReactNative = require('react-native');
const PropTypes = require('prop-types');
const createReactClass = require('create-react-class');
const {
View,
Animated,
StyleSheet,
ScrollView,
Text,
Platform,
Image,
Dimensions,
TouchableOpacity
} = ReactNative;
import screen from '../../util/Screen'
const WINDOW_WIDTH = Dimensions.get('window').width;
const menuItems = [
{
image:require('../../image/court_announce.png'),
title:'法院公告'
},
{
image:require('../../image/house_value.png'),
title:'房产价值'
},
{
image:require('../../image/school.png'),
title:'学校'
},
{
image:require('../../image/traffic_icon.png'),
title:'交通'
},
{
image:require('../../image/life_icon.png'),
title:'生活'
},
{
image:require('../../image/medical.png'),
title:'医疗'
},
]
export default class MyScrollTabBar extends Component{
// 构造
constructor(props) {
super(props);
this.necessarilyMeasurementsCompleted = this.necessarilyMeasurementsCompleted.bind(this)
this.onTabContainerLayout = this.onTabContainerLayout.bind(this)
this.onContainerLayout = this.onContainerLayout.bind(this)
this.updateTabPanel = this.updateTabPanel.bind(this)
this.updateTabUnderline = this.updateTabUnderline.bind(this)
this.updateView = this.updateView.bind(this)
// 初始状态
this._tabsMeasurements = [];
this.state = {
_leftTabUnderline: new Animated.Value(0),
// _widthTabUnderline: new Animated.Value(0),
_containerWidth: null,
};
}
componentDidMount() {
this.props.scrollValue.addListener(this.updateView);
}
updateView(offset) {
console.log('offset',offset)
const position = Math.floor(offset.value);
const pageOffset = offset.value % 1;
const tabCount = menuItems.length;
const lastTabPosition = tabCount - 1;
console.log('position',position)
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, isLastTab) {
return this._tabsMeasurements[position] &&
(isLastTab || this._tabsMeasurements[position + 1]) &&
this._tabContainerMeasurements &&
this._containerMeasurements;
}
//更新tab
updateTabPanel(position, pageOffset) {
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;
// center tab and smooth tab change (for when tabWidth changes a lot between two tabs)
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, pageOffset, tabCount) {
const lineLeft = this._tabsMeasurements[position].left;
const lineRight = this._tabsMeasurements[position].right;
if (position < tabCount - 1) {
const nextTabLeft = this._tabsMeasurements[position + 1].left;
const nextTabRight = this._tabsMeasurements[position + 1].right;
const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft);
const newLineRight = (pageOffset * nextTabRight + (1 - pageOffset) * lineRight);
this.state._leftTabUnderline.setValue(newLineLeft);
// this.state._widthTabUnderline.setValue(newLineRight - newLineLeft);
} else {
this.state._leftTabUnderline.setValue(lineLeft);
// this.state._widthTabUnderline.setValue(lineRight - lineLeft);
}
}
renderTab(item, page, isTabActive, onPressHandler,onLayoutHandler) {
const { activeTextColor, inactiveTextColor, textStyle,activeBgColor,inactiveBgColor } = this.props;
const textColor = isTabActive ? activeTextColor : inactiveTextColor;
const fontWeight = isTabActive ? 'bold' : 'normal';
const tabBackgroundColor = isTabActive ? activeBgColor : inactiveBgColor
return <TouchableOpacity
key={`page_${page}`}
onPress={() => onPressHandler(page)}
onLayout={onLayoutHandler}
>
<View style={{width:screen.width/4,height:screen.PIXEL_120 + 10 ,justifyContent:'center',alignItems:'center',
borderBottomColor:'#fff'
}}
>
<Image source={item.image} style={{width:screen.PIXEL_40,height:screen.PIXEL_40}}/>
<Text style={{fontSize:16,color:'#fff',marginTop:screen.PIXEL_10}}>{item.title}</Text>
</View>
</TouchableOpacity>;
}
measureTab(page, event) {
const { x, width, height, } = event.nativeEvent.layout;
console.log('tabLayout',event.nativeEvent.layout)
this._tabsMeasurements[page] = {left: x, right: x + width, width, height, };
this.updateView({value: this.props.scrollValue.__getValue(), });
}
render() {
const tabUnderlineStyle = {
position: 'absolute',
height: 4,
backgroundColor: '#fff',
bottom: 0,
width: screen.width/4,
};
const dynamicTabUnderline = {
left: this.state._leftTabUnderline,
};
return (
<View
style={[styles.container, {backgroundColor:'#4994ea', }, this.props.style, ]}
onLayout={this.onContainerLayout}
>
<ScrollView
ref={(scrollView) => { this._scrollView = scrollView; }}
horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
directionalLockEnabled={true}
bounces={false}
scrollsToTop={false}
>
<View
style={styles.tabs}
onLayout={this.onTabContainerLayout}
>
{menuItems.map((item, page) => {
const isTabActive = this.props.activeTab === page;
return this.renderTab(item, page, isTabActive, this.props.goToPage,this.measureTab.bind(this, page));
})}
<Animated.View style={[tabUnderlineStyle, dynamicTabUnderline, this.props.underlineStyle, ]} />
</View>
</ScrollView>
</View>
)
}
componentWillReceiveProps(nextProps) {
// If the tabs change, force the width of the tabs container to be recalculated
if (JSON.stringify(this.props.tabs) !== JSON.stringify(nextProps.tabs) && this.state._containerWidth) {
this.setState({ _containerWidth: null, });
}
}
//获取容器的总宽度
onTabContainerLayout(e) {
this._tabContainerMeasurements = e.nativeEvent.layout;
let width = this._tabContainerMeasurements.width;
console.log('tabWidth',width)
if (width < WINDOW_WIDTH) {
// width = WINDOW_WIDTH;
}
this.setState({ _containerWidth: width, });
this.updateView({value: this.props.scrollValue.__getValue(), });
}
onContainerLayout(e) {
this._containerMeasurements = e.nativeEvent.layout;
let width = this._containerMeasurements.width;
console.log('containerWidth',width)
this.updateView({value: this.props.scrollValue.__getValue(), });
}
}
const styles = StyleSheet.create({
container: {
height: screen.PIXEL_120 + 10,
borderWidth: 1,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderColor: '#ccc',
},
tabs: {
flexDirection: 'row',
},
});
调用
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View,ScrollView,Image,TouchableWithoutFeedback} from 'react-native';
import ScrollableTabView,{ScrollableTabBar,DefaultTabBar} from 'react-native-scrollable-tab-view'
import MyScrollTabBar from '../app/components/DetailSecond/MyScrollTabBar'
import CourtAnnounce from '../app/components/DetailSecond/CourtAnnounce'
import HouseValueDetail from '../app/components/DetailSecond/HouseValueDetail'
import SchoolView from '../app/components/DetailSecond/SchoolView'
import TrafficView from '../app/components/DetailSecond/TrafficView'
import LifeView from '../app/components/DetailSecond/LifeView'
import HospitalView from '../app/components/DetailSecond/HospitalView'
export default class ScrollTabDemo extends Component{
render(){
return (
<ScrollableTabView
renderTabBar={() => <MyScrollTabBar/>}>
{/* <Text tabLabel='Tab1'/>
<Text tabLabel='Tab2'/>
<Text tabLabel='Tab3'/>
<Text tabLabel='Tab4'/>
<Text tabLabel='Tab5'/>
<Text tabLabel='Tab6'/>*/}
<CourtAnnounce/>
<HouseValueDetail/>
<SchoolView/>
<TrafficView/>
<LifeView/>
<HospitalView/>
</ScrollableTabView>
);
}
}
我用的RN版本是0.58.4,"react-native-scrollable-tab-view": "^0.10.0",
其中遇到了一个很蛋疼的bug,有必要吐槽下,就是在左右滚动页面的过程中上面的tabbar不能跟着滚动,而且手机会卡死重启,as中打印日志如下
30211555243190_.pic_hd.jpg
说是主线程做了太多任务,好奇怪 ,但是当我打开调试模式debug js remotely 就会正常运行了,一开始以为是RN版本的问题,去找作者的github上找issue,好像也没有人遇到过这个问题,后来我乱试一气,终于招到了原因
<ScrollableTabView
// tabBarPosition='top'
prerenderingSiblingsNumber={2}
// onChangeTab={(index) => {
//
// console.log(index)
//
// }}
initialPage={this.state.currentPage}
renderTabBar={()=>(<MyScrollTabBar/>)}
>
原来是因为使用了 onChangeTab这个方法,把它注释掉就可以正常运行了,因为项目需求是导航标题要跟着页面切换,所以要使用这个方法,但是那个bug比起这个需求,当然是解决Bug重要了。
希望能帮助到遇到同样问题的小伙伴。