React Native带你实现scrollable-tab-view(二)

上一节React Native带你实现scrollable-tab-view(一)中我们最后实现了我们scrollable-tab-view的效果为:
这里写图片描述

代码为:

/**
 * @author YASIN
 * @version [React-Native Pactera V01, 2017/9/5]
 * @date 2017/9/5
 * @description index
 */
......

    render() {
        return (
            <View
                style={styles.container}
            >
                {/*渲染tabview*/}
                {this._renderTabView()}
                <ScrollView
                    style={styles.scrollStyle}
                    pagingEnabled={true}
                    horizontal={true}
                >
                    {['页面一', '页面二', '页面三'].map((item, index)=> {
                        return (
                            <Text
                                key={item + index}
                                style={{
                                    width: screenW,
                                    flex: 1,
                                }}
                            >
                                {item}
                            </Text>
                        );
                    })}
                </ScrollView>
            </View>
        );
    }

    /**
     * 渲染tabview
     * @private
     */
    _renderTabView() {
        return (
            <View
                style={styles.tabContainer}
            >
                {['页面一', '页面二', '页面三'].map((item, index)=> {
                    return (
                        <TouchableOpacity
                            key={item + index}
                            style={styles.tabStyle}
                        >
                            <Text>{item}</Text>
                        </TouchableOpacity>
                    );
                })}
            </View>
        );
    }
}
......

作为一个第三方性的组件,这样写耦合性太高,不太容易复用,所以我们改改:
index.js:

export default class ScrollableTab extends Component {
    static propTypes = {}
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {};
    }

    render() {
        return (
            <View
                style={styles.container}
            >
                {/*渲染tabview*/}
                {this._renderTabView()}
                {/*渲染主体内容*/}
                {this._renderScrollableContent()}
            </View>
        );
    }

    /**
     * 渲染tabview
     * @private
     */
    _renderTabView() {
        let tabParams = {
            tabs: this._children().map((child)=>child.props.tabLabel),
        };
        return (
            <DefaultTabBar
                {...tabParams}
            />
        );
    }

    /**
     * 渲染主体内容
     * @private
     */
    _renderScrollableContent() {
        return (
            <Animated.ScrollView
                style={styles.scrollStyle}
                pagingEnabled={true}
                horizontal={true}
            >
                {this.props.children}
            </Animated.ScrollView>
        );
    }

    /**
     * 获取子控件数组集合
     * @param children
     * @returns {*}
     * @private
     */
    _children(children = this.props.children) {
        return React.Children.map(children, (child)=>child);
    }
}

然后把之前的渲染_renderTabView的代码变成了一个单独的组件DefaultTabBar.js,然后传入tabs即为我们需要的tab内容:

DefaultTabBar.js:

/**
 * @author YASIN
 * @version [React-Native Pactera V01, 2017/9/5]
 * @date 17/2/23
 * @description DefaultTabBar
 */
import React, {
    Component, PropTypes,
} from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    Dimensions,
} from 'react-native';
const screenW = Dimensions.get('window').width;
const screenH = Dimensions.get('window').height;
export default class DefaultTabBar extends Component {
    static propTypes = {
        tabs: PropTypes.array,
    }
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {};
    }

    render() {
        return (
            <View style={styles.container}>
                {this.props.tabs.map((name, page) => {
                    return this._renderTab(name, page);
                })}
            </View>
        );
    }

    /**
     * 渲染tab
     * @param name 名字
     * @param page 下标
     * @private
     */
    _renderTab(name, page) {
        return (
            <TouchableOpacity
                key={name + page}
                style={styles.tabStyle}
            >
                <Text>{name}</Text>
            </TouchableOpacity>
        );
    }
}
const styles = StyleSheet.create({
    container: {
        width: screenW,
        flexDirection: 'row',
        alignItems: 'center',
        height: 50,
    },
    tabStyle: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
});

然后我们用的时候只需要渲染自己的内容模块就可以了:

export default class ScrollTabDemo extends Component {
    render() {
        return (
            <View style={styles.container}>
                <ScrollableTab>
                    {['页面一', '页面二', '页面三'].map((item, index)=> {
                        return (
                            <Text
                                tabLabel={item}
                                key={item + index}
                                style={{
                                    width: screenW,
                                    flex: 1,
                                }}
                            >
                                {item}
                            </Text>
                        );
                    })}
                </ScrollableTab>
                {/*<App/>*/}
            </View>
        );
    }
}

哈哈~~ 是不是很熟悉呢? scrollable-tab-view第三方库就是这么用的,没有啥高大上的东西,只是把公共部分提取出来了,这也就是自定义组件存在的目的。

然后运行代码,跟我们一开始一样的效果~~~

接下来我们得知道我们每次滑动scrollview后我们停留在哪个页面,主要就是用到scrollview的onMomentumScrollBegin跟onMomentumScrollEnd属性,这两个是干嘛的呢?就是当滑动开始跟滑动结束时候的回调。

怎么拿到我们的当前页面呢?
onMomentumScrollBegin跟onMomentumScrollEnd会返回event,event里面包含scrollview在y轴的偏移量,Math.round(y轴的偏移量/控件的宽度)即为当前页面。

有了思路我们就开动了哈~

获取控件的宽度应该是没问题哈,我们定义一个叫containerWidth的变量,然后默认宽度为屏幕宽:

render() {
        return (
            <View
                style={styles.container}
                onLayout={this._onLayout}
            >
                {/*渲染tabview*/}
                {this._renderTabView()}
                {/*渲染主体内容*/}
                {this._renderScrollableContent()}
            </View>
        );
    }
/**
     * 获取控件宽度
     * @param e
     * @private
     */
    _onLayout = (e)=> {
        let {width}=e.nativeEvent.layout;
        if (this.state.containerWidth !== width) {
            this.setState({
                containerWidth: width,
            });
        }
    }

好了,我们已经获取到我们控件的宽度了,于是我们开始处理_onMomentumScrollBeginAndEnd方法:

  /**
     * 渲染主体内容
     * @private
     */
    _renderScrollableContent() {
        return (
            <Animated.ScrollView
                style={{width: this.state.containerWidth}}
                pagingEnabled={true}
                horizontal={true}
                onMomentumScrollBegin={this._onMomentumScrollBeginAndEnd}
                onMomentumScrollEnd={this._onMomentumScrollBeginAndEnd}
            >
                {this.props.children}
            </Animated.ScrollView>
        );
    }
 /**
     * scrollview开始跟结束滑动回调
     * @param e
     * @private
     */
    _onMomentumScrollBeginAndEnd = (e) => {
        let offsetX = e.nativeEvent.contentOffset.x;
        let page = Math.round(offsetX / this.state.containerWidth);
        if (this.state.currentPage !== page) {
            console.log('当前页面-->'+page);
            this.setState({
                currentPage: page,
            });
        }
    }

我们运行代码:

这里写图片描述

既然我们拿到了当前当前页面页码,然后我们试着把对应的tab置为红色,没选中的为绿色:

于是我们把页码传入DefaultTabView:

 /**
     * 渲染tabview
     * @private
     */
    _renderTabView() {
        let tabParams = {
            tabs: this._children().map((child)=>child.props.tabLabel),
            activeTab: this.state.currentPage,
        };
        return (
            <DefaultTabBar
                {...tabParams}
                style={[{width: this.state.containerWidth}]}
            />
        );
    }

然后在DefaultTabView中,如果当前tab的下标为activeTab就显示红色:

DefaultTabView.js:

/**
 * @author YASIN
 * @version [React-Native Pactera V01, 2017/9/5]
 * @date 17/2/23
 * @description DefaultTabBar
 */
import React, {
    Component, PropTypes,
} from 'react';
import {
    View,
    Text,
    StyleSheet,
    TouchableOpacity,
    Dimensions,
} from 'react-native';
const screenW = Dimensions.get('window').width;
const screenH = Dimensions.get('window').height;
export default class DefaultTabBar extends Component {
    static propTypes = {
        tabs: PropTypes.array,
        activeTab: PropTypes.number,//当前选中的tab
        style: View.propTypes.style,
    }
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {};
    }

    render() {
        return (
            <View style={[styles.container, this.props.style]}>
                {this.props.tabs.map((name, page) => {
                    const isTabActive = this.props.activeTab === page;
                    return this._renderTab(name, page, isTabActive);
                })}
            </View>
        );
    }

    /**
     * 渲染tab
     * @param name 名字
     * @param page 下标
     * @param isTabActive 是否是选中的tab
     * @private
     */
    _renderTab(name, page, isTabActive) {
        let tabTextStyle = null;
        //如果被选中的style
        if (isTabActive) {
            tabTextStyle = {
                color:'green'
            };
        } else {
            tabTextStyle = {
                color:'red'
            };
        }
        return (
            <TouchableOpacity
                key={name + page}
                style={[styles.tabStyle]}
            >
                <Text style={[tabTextStyle]}>{name}</Text>
            </TouchableOpacity>
        );
    }
}
const styles = StyleSheet.create({
    container: {
        width: screenW,
        flexDirection: 'row',
        alignItems: 'center',
        height: 50,
    },
    tabStyle: {
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
    }
});

然后运行代码:

这里写图片描述

可以看到,我们简单的就可以玩起来了,好啦~ 这一节先到这里了,下一节我们来完成点击tab切换到指定页面,然后把tabview的指示线显示出来。

欢迎入群,欢迎交流,大牛勿喷~~

猜你喜欢

转载自blog.csdn.net/vv_bug/article/details/77857730