react-native-scrollable-tab-view

2019-04-14  本文已影响0人  MasterPaul
效果 QQ20190414-212423-HD.gif
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重要了。

希望能帮助到遇到同样问题的小伙伴。

上一篇下一篇

猜你喜欢

热点阅读