UNPKG

hcmobile-sdk

Version:

mobile-sdk

219 lines (198 loc) 8.01 kB
/** * Created by tangzhibin on 16/2/28. */ 'use strict' import React, { Component } from 'react' import PropTypes from 'prop-types' import { StyleSheet, View, ViewPropTypes, Text, TouchableOpacity,ScrollView ,Dimensions} from 'react-native' import IndicatorViewPager from '../IndicatorViewPager' const itemLayoutInfo = []; const screenWidth = Dimensions.get('window').width; const FORWARD = 1; const NONE = 0; const BACKWARD = -1; const DEFAULT_ITEM_MARGIN = 20; export default class PagerTitleIndicator extends Component { static propTypes = { ...ViewPropTypes, initialPage: PropTypes.number, pager: PropTypes.instanceOf(IndicatorViewPager), titles: PropTypes.arrayOf(PropTypes.string).isRequired, itemStyle: ViewPropTypes.style, selectedItemStyle: ViewPropTypes.style, itemTextStyle: Text.propTypes.style, trackScroll:PropTypes.bool, selectedItemTextStyle: Text.propTypes.style, selectedBorderStyle: ViewPropTypes.style, renderTitle: PropTypes.func } static defaultProps = { titles: [], initialPage: 0 } state = { selectedIndex: this.props.initialPage } constructor(props) { super(props); this._preSelectedIndex = props.initialPage; this._contentHorOffset = 0; this._currentMaxHor = screenWidth; this._titleCount = props.titles.length || 0; } shouldComponentUpdate (nextProps, nextState) { return this.state.selectedIndex != nextState.selectedIndex || this.props.titles + '' != nextProps.titles + '' || this.props.style != nextProps.style || this.props.itemStyle != nextProps.itemStyle || this.props.itemTextStyle != nextProps.itemTextStyle || this.props.selectedItemTextStyle != nextProps.selectedItemTextStyle || this.props.selectedBorderStyle != nextProps.selectedBorderStyle } componentWillReceiveProps(nextProps){ this._titleCount = nextProps.titles.length; } render () { let {titles, pager, itemStyle, selectedItemStyle, itemTextStyle, selectedItemTextStyle, selectedBorderStyle} = this.props if (!titles || titles.length === 0)return null let titleViews = titles.map((title, index) => { let isSelected = this.state.selectedIndex === index const itemMargin = (itemStyle && itemStyle.marginLeft) || DEFAULT_ITEM_MARGIN; let itemMarginObj = null; if (!itemStyle && !selectedItemStyle) { itemMarginObj = index < this._titleCount - 1 ? { marginLeft: itemMargin } : { marginLeft: itemMargin, marginRight: itemMargin }; } const titleView = this.props.renderTitle ? this.props.renderTitle(index, title, isSelected) : ( <Text style={isSelected ? [styles.titleTextSelected, selectedItemTextStyle] : [styles.titleText, itemTextStyle]} > {title} </Text> ) return ( <TouchableOpacity style={[styles.titleContainer, itemMarginObj, isSelected ? selectedItemStyle : itemStyle]} activeOpacity={0.6} key={index} onLayout={e => { itemLayoutInfo[index] = e.nativeEvent; }} onPress={() => { if(this.props.trackScroll === true){ this._visibleDetect(index); } !isSelected && pager.setPage(index)} } > {titleView} {isSelected ? <View style={[styles.selectedBorder, selectedBorderStyle]} /> : null} </TouchableOpacity> ) }) return ( <View style={[styles.indicatorContainer, this.props.style]} > <ScrollView scrollEventThrottle={1} onScroll={e => { this._contentHorOffset = e.nativeEvent.contentOffset.x; this._currentMaxHor = screenWidth + this._contentHorOffset; }} showsHorizontalScrollIndicator={false} ref={c => { this.scroller = c; }} horizontal={true} style={{ flex: 1 }} > {titleViews} </ScrollView> </View> ) } _visibleDetect(selectedIndex) { if(selectedIndex > this._titleCount -1 ) return ; const curItemLayoutInfo = itemLayoutInfo[selectedIndex]; const { width, x: curItemOffsetX } = curItemLayoutInfo.layout; const curItemAbsPosition = width + curItemOffsetX + DEFAULT_ITEM_MARGIN; // add on margin let moveDir = NONE; if (selectedIndex > this._preSelectedIndex) { moveDir = FORWARD; } else if (selectedIndex < this._preSelectedIndex) { moveDir = BACKWARD; } if (moveDir === FORWARD) { //indicator move forward let lastItemOffsetX; let lastItemAbsPosition; if (selectedIndex < this._titleCount - 1) { const nextLayoutInfo = itemLayoutInfo[selectedIndex + 1]; const width = nextLayoutInfo.layout.width; lastItemOffsetX = nextLayoutInfo.layout.x; lastItemAbsPosition = width + lastItemOffsetX + DEFAULT_ITEM_MARGIN; } else if (selectedIndex === this._titleCount - 1) { lastItemOffsetX = curItemOffsetX; lastItemAbsPosition = curItemAbsPosition; } if (this._contentHorOffset > lastItemOffsetX) { const deltaX = curItemOffsetX - DEFAULT_ITEM_MARGIN; this.scroller.scrollTo({ x: deltaX }); } else if (this._currentMaxHor < lastItemAbsPosition) { const deltaX = lastItemAbsPosition - this._currentMaxHor; this.scroller.scrollTo({ x: this._contentHorOffset + deltaX }); } } else if (moveDir === BACKWARD) { //indicator move back let lastItemOffsetX; let lastItemAbsPosition; if (selectedIndex > 0) { const nextLayoutInfo = itemLayoutInfo[selectedIndex - 1]; const width = nextLayoutInfo.layout.width; lastItemOffsetX = nextLayoutInfo.layout.x; lastItemAbsPosition = width + lastItemOffsetX + DEFAULT_ITEM_MARGIN; } else if (selectedIndex === 0) { lastItemOffsetX = curItemOffsetX; lastItemAbsPosition = curItemAbsPosition; } if (this._contentHorOffset > lastItemOffsetX || this._currentMaxHor < curItemAbsPosition) { const deltaX = lastItemOffsetX - DEFAULT_ITEM_MARGIN; this.scroller.scrollTo({ x: deltaX }); } } } onPageSelected (e) { if(this.props.trackScroll === true){ this._visibleDetect(e.position); } this._preSelectedIndex = e.position; this.setState({selectedIndex: e.position}) } } const styles = StyleSheet.create({ indicatorContainer: { height: 40, flexDirection: 'row', backgroundColor: '#F6F6F6' }, titleText: { color: '#333333', fontSize: 15 }, titleTextSelected: { color: '#FF7200', fontSize: 15 }, titleContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' }, selectedBorder: { backgroundColor: '#FF7200', height: 2, position: 'absolute', bottom: 0, left: 0, right: 0 } })