React Native学习之ViewPagerAndroid做引导页

实战:使用ViewPagerAndroid实现引导页

涉及的知识点:

  • 组件如何联动
  • 符合ES6的标准写法
  • 组件的封装,非常灵活
  • Navigator路由的用法

index.android.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    Image,
    TouchableOpacity,
    ViewPagerAndroid,
    Navigator,
    View
} from 'react-native';

import LikeCount from './LikeCount.js';  //导入“喜欢数”组件
import Button from './Button.js';  //导入“自定义的按钮”组件
import HomeUI from './HomeUI.js';  //导入“首页”组件

const PAGES = 5;

const BGCOLOR = ['#fdc08e', '#fff6b9', '#99d1b7', '#dde5fe', '#f79273'];

const IMAGE_URIS = [
  'http://apod.nasa.gov/apod/image/1410/20141008tleBaldridge001h990.jpg',
  'http://apod.nasa.gov/apod/image/1409/volcanicpillar_vetter_960.jpg',
  'http://apod.nasa.gov/apod/image/1409/m27_snyder_960.jpg',
  'http://apod.nasa.gov/apod/image/1409/PupAmulti_rot0.jpg',
  'https://www.baidu.com/img/bd_logo1.png',
];

//进度条组件
class ProgressBar extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        //当前位置+偏移量
        var fractionalPosition = (this.props.progress.position + this.props.progress.offset);
        var progressBarSize = (fractionalPosition / (PAGES - 1)) * this.props.size;
        return(
            //进度条使用2个View实现,通过宽度表示进度
            //progressBarSize当前的进度
            //this.props.size进度条的总长度
            <View style={[styles.progressBarContainer, {width: this.props.size}]}>
                <View style={[styles.progressBar, {width: progressBarSize}]}/>
            </View>
        );
    }
}

class WelcomeUI extends Component {
    //欢迎页面,使用Viewpager实现

    //null is not an object 解决办法:初始化的时候要用constructor 而不是getInitialState
    //当前使用 ES6 作为标准编程规范

    //React Native 组件中必须使用构造函数初始化 state

    //用构造函数来替代之前的 Initial实例化
    constructor(props) {
        super(props);
        this.state = {
            page: 0,
            animationsAreEnabled: true, //动画是否开启
            progress: {
                position: 0,
                offset: 0,
            },
        };
    }

    //See React on ES6+
    onPageSelected = (e)=>{
        //这个回调会在页面切换完成后(当用户在页面间滑动)调用
        //回调参数中的event.nativeEvent对象
        this.setState({page: e.nativeEvent.position});
    }

    onPageScroll = (e)=>{
        //当在页间切换时(不论是由于动画还是由于用户在页间滑动/拖拽)执行。
        //回调参数中的event.nativeEvent对象会包含如下数据:
        //position: 从左数起第一个当前可见的页面的下标
        //offset: 一个在[0,1)(大于等于0,小于1)之间的范围,代表当前页面切换的状态。值x表示现在"position"所表示的页有(1 - x)的部分可见,而下一页有x的部分可见。
        this.setState({progress:e.nativeEvent});
    }

    move(delta) {
        var page = this.state.page + delta;
        this.go(page);
    }

    go(page) {
        if(this.state.animationsAreEnabled) {
            this.viewPager.setPage(page);
        } else {
            this.viewPager.setPageWithoutAnimation(page);
        }
        //刷新
        this.setState({page});
    }

    onClick = ()=>{
        //alert('点击');
        const { navigator } = this.props;
        //为什么这里可以取得 props.navigator?请看上文:
        //<Component {...route.params} navigator={navigator} />
        //这里传递了navigator作为props
        if(navigator) {
            navigator.push({
                name: 'HomeUI',
                component: HomeUI,
           })
        }
    }

    render() {
        const thunbsUp = '\uD83D\uDC4D';
        var pages=[];
        for(var i=0; i<PAGES; i++) {
            var pageStyle = {
                backgroundColor: BGCOLOR[i % BGCOLOR.length],
                alignItems: 'center',
                padding: 20,
            };
            if (i < PAGES-1) {
                //前面几个Viewpage
                //collapsable: 如果一个View只用于布局它的子组件,则它可能会为了优化而从原生布局树中移除。
                //把此属性设为false可以禁用这个优化,以确保对应视图在原生结构中存在。
                pages.push(
                    <View key={i} style={pageStyle} collapsable={false}>
                        <Image
                            style={styles.image}
                            source={{uri: IMAGE_URIS[i % BGCOLOR.length]}}
                            />
                        <LikeCount />
                    </View>
                );
            } else {
                //最后一个viewpage 加了一个按钮
                pages.push(
                    <View key={i} style={pageStyle} collapsable={false}>
                        <Image
                            style={styles.image}
                            source={{uri: IMAGE_URIS[i % BGCOLOR.length]}}
                            />
                        <LikeCount />
                        <TouchableOpacity onPress={this.onClick} style={styles.startupButton}>
                            <Text style={styles.likesText}> {thunbsUp + '启动首页'} </Text>
                        </TouchableOpacity>
                    </View>
                );
            }
        }

        var { page, animationsAreEnabled } = this.state;
        //等效于
        //var page = this.state.page;
        //var animationsAreEnabled = this.state.animationsAreEnabled;

        return (
            <View style={styles.container}>
                <ViewPagerAndroid
                    style={styles.viewPager}
                    initialPage={0}
                    onPageScroll={this.onPageScroll}
                    onPageSelected={this.onPageSelected}
                    ref={viewPager => { this.viewPager = viewPager; }}
                    >
                    {pages}
                </ViewPagerAndroid>

                <View style={styles.buttons}>
                    { animationsAreEnabled ?
                    <Button
                        text="Turn off animations"
                        enabled={true}
                        onPress={() => this.setState({animationsAreEnabled: false})}
                        /> :
                    <Button
                        text="Turn animations back on"
                        enabled={true}
                        onPress={() => this.setState({animationsAreEnabled: true})}
                        />
                    }
                </View>

                <View style={styles.buttons}>
                    <Button text="Start" enabled={page > 0} onPress={() => this.go(0)}/>
                    <Button text="Prev" enabled={page > 0} onPress={() => this.move(-1)}/>

                    <Text style={styles.buttonText}>页:{page + 1} / {PAGES}</Text>
                    <ProgressBar size={100} progress={this.state.progress}/>

                    <Button text="Next" enabled={page < PAGES - 1} onPress={() => this.move(1)}/>
                    <Button text="Last" enabled={page < PAGES - 1} onPress={() => this.go(PAGES - 1)}/>
                 </View>
            </View>
        );
    }
}

class RNAPP extends Component {
    render() {
        let defaultName='WelcomeUI';
        let defaultComponent=WelcomeUI;
        return (
            <Navigator
                initialRoute={{ name: defaultName, component: defaultComponent }}
                //配置场景
                configureScene={
                    (route) => {
                        //页面之间跳转的动画,具体值可以查看源代码: node_modules/react-native/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js
                        return Navigator.SceneConfigs.FloatFromRight;
                    }
                }
                renderScene={
                    (route, navigator) => {
                        let Component = route.component;
                        return <Component {...route.params} navigator={navigator} />
                    }
                }
            />
        );
    }
}

const styles = StyleSheet.create({
    buttons: {
        flexDirection: 'row',
        height: 30,
        backgroundColor: 'black',
        alignItems: 'center',
        justifyContent: 'space-between',
    },
    container: {
        flex: 1,
        backgroundColor: 'white',
    },
    image: {
        width: 300,
        height: 200,
        padding: 20,
    },
    startupButton:{
        backgroundColor: 'rgba(0, 0, 0, 0.1)',
        borderColor: '#333333',
        borderWidth: 1,
        borderRadius: 5,
        margin: 8,
        padding: 8,
    },
    progressBarContainer: {
        height: 10,
        margin: 10,
        borderColor: '#eeeeee',
        borderWidth: 2,
    },
    progressBar: {
        alignSelf: 'flex-start',
        flex: 1,
        backgroundColor: '#ff0000',
    },
    viewPager: {
        flex: 1,
    },
    buttonText: {
        color: 'white',
    },
});

AppRegistry.registerComponent('RNAPP', () => RNAPP);

LikeCount.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    TouchableOpacity,
    View
} from 'react-native';

//点赞数组件,ES6语法
export default class LikeCount extends Component {
    constructor(props){
        super(props);
            this.state={
            likes:0,
        };
    }

    onClick = ()=>{
        this.setState({likes: this.state.likes + 1});
    }

    render() {
        //点赞的小图标,emoji表情,使用unicode编码
        const thunbsUp='\uD83D\uDC4D';
        return (
            <View style={styles.likeContainer}>
                <TouchableOpacity onPress={this.onClick} style={styles.likeButton}>
                    <Text style={styles.likesText}>{thunbsUp+'Like'}</Text>
                </TouchableOpacity>
                <Text style={styles.likesText}>{this.state.likes+'个喜欢数'}</Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    likeButton: {
        backgroundColor: 'rgba(0, 0, 0, 0.1)',
        borderColor: '#333333',
        borderWidth: 1,
        borderRadius: 5,
        flex: 1,
        margin: 8,
        padding: 8,
    },
    likeContainer: {
        flexDirection: 'row',
    },
    likesText: {
        flex: 1,
        fontSize: 18,
        alignSelf: 'center',
    },
});

Button.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    TouchableWithoutFeedback,
    View
} from 'react-native';

//开始 前进 后退 最后 开启/关闭动画 5个按钮的通用写法
export default class Button extends Component {
    constructor(props) {
        super(props);
    }

    _handlePress = ()=>{
        if (this.props.enabled && this.props.onPress) {
            //按钮可用,没有变灰,则启用按钮的onPress()方法
            this.props.onPress();
        }
    }

    render() {
        //这个View有2个样式,第二个样式是用来覆盖的(加了背景颜色和透明度)
        return(
            <TouchableWithoutFeedback onPress={this._handlePress}>
                <View style={[styles.button, this.props.enabled ? {} : styles.buttonDisabled]}>
                    <Text style={styles.buttonText}>{this.props.text}</Text>
                </View>
            </TouchableWithoutFeedback>
        );
    }
}

const styles = StyleSheet.create({
    button: {
        flex: 1,
        width: 0,
        margin: 5,
        borderColor: 'gray',
        borderWidth: 1,
        backgroundColor: 'gray',
    },
    buttonDisabled: {
        backgroundColor: 'black',
        opacity: 0.5,
    },
    buttonText: {
        color: 'white',
    },
});

Button.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, { Component } from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View
} from 'react-native';

export default class HomeUI extends Component {
    goBack() {
        const { navigator } = this.props;
        if(navigator) {
            //很熟悉吧,入栈出栈~ 把当前的页面pop掉,即可返回上一个页面
            navigator.pop();
        }
    }

    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.welcome} onPress={this.goBack.bind(this)}>
                    Welcome to React Native!{'\n'}
                </Text>
                <Text style={styles.instructions}>
                    To get started, edit index.android.js
                </Text>
                <Text style={styles.instructions}>
                    Double tap R on your keyboard to reload,{'\n'}
                    Shake or press menu button for dev menu{'\n'}
                    Powered by rnapp.cc
                </Text>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
    },
});

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/02/25/react-native-learning-viewpagerandroid-as-guide-page/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
React Native学习之ViewPagerAndroid做引导页
实战:使用ViewPagerAndroid实现引导页 涉及的知识点: 组件如何联动 符合ES6的标准写法 组件的封装,非常灵活 Navigator路由的用法 index.android.js /** ……
<<上一篇
下一篇>>
文章目录
关闭
目 录