一个完整的RN项目总结

本文分享了React Native应用开发中的七个实用技巧,包括如何仅在首次启动应用时显示引导页、实现启动页图片全屏显示、添加侧滑删除功能、制作可点击的轮播图、实现文字竖向滚动效果、解决FlatList上拉加载更多的问题以及使用Modal创建浮动弹窗。

直接从进入app开始讲每一个功能的具体实现。

先声明本文提到的这些功能,我的实现方法可能并不是最优的,如果有更好的实现方法,欢迎交流!

转载请注明出处!

1.引导页只在第一次打开app后出现:

实现方法:类似于原生Android的处理方法,直接存一个值到本地,原生用的是Sharedpreferences,RN里面就直接用AsyncStorage。原理就是在第一次打开的时候设定一个值保存在本地,之后打开再取出这个值,如果不为之前设定的那个值就显示不同的内容。

实现代码:我这里是根据需求进入不同的路由,具体操作可以自己设置

openApp(){
        AsyncStorage.getItem('isFirst',(error,result)=>{
            if (result === 'false') {
                this.props.navigator.resetTo({ component:Root }) }
            else { console.log('第一次打开');
                AsyncStorage.setItem('isFirst','false',(error)=>{ if (error) { alert(error); } });
                this.props.navigator.resetTo({ component:Root2 })}});
}

2.启动页图片铺满屏幕:

实现方法:设置图片的style

实现代码:

image:{
        flex:1,
        alignItems:'center',
        justifyContent:'center',
        width:null,
        height:null,
    }

3.类似qq消息列表的侧滑删除:

实现方法:列表用的是FlatList,实现侧滑删除用到的了swipable,每个Item都给它在外层包裹一层swipeable,使得每个Item都是能够左滑的,然后在Swipeable中给rightButtons设置样式、点击事件,来实现侧滑删除的功能。

下载:npm install --save react-swipeable

导入:import Swipeable from 'react-native-swipeable';

实现代码:FlatList不用改,直接在renderItem上操作

<FlatList
                    renderItem={this.ViewList}
                    data={this.state.dataArray}
                    keyExtractor={this._keyExtractor}
                    ItemSeparatorComponent={this.ItemSeparatorComponent}
                />
ViewList = ({item}) => {
            return (
                <Swipeable
                    rightButtons={[
                        <TouchableOpacity
                            style={styles.swipeableStyle}
                            onPress={() => {
                                this._delete(item.value.id);
                                //这里我是把这个Item的Id发给后台,后台删除后我再重新渲染页面
                            }}
                        >
                            <Text style={{fontSize: 20, color: 'black'}}>删除</Text>
                        </TouchableOpacity>
                    ]}
                >
                    <View style={styles.wrap}>
                        //这里是具体Item的样式
                    </View>
                </Swipeable>

4.轮播图(点击轮播图的图片可跳转):

实现方法:轮播图用到是swiper,实现点击跳转是直接在Image外加一层TouchableWithoutFeedback,然后再写点击事件就行了

下载:npm install react-native-swiper --save

导入:import Swiper from 'react-native-swiper';

实现代码:

<Swiper
                        style={styles.wrap2}
                        loop={true}//如果设置为false,那么滑动到最后一张时,再次滑动将不会展示第一张图片
                        autoplay={true}//设置为true,则页面可以自动轮播
                        autoplayTimeout={3}//设置每个页面自动滑动停留的时间
                        horizontal={true}//如果值为true时,那么滚动的内容将是横向排列的,而不是垂直于列中的
                        paginationStyle={{bottom:ScreenUtils.scaleSize(7)}}//设置页面原点的样式
                        dotStyle={{backgroundColor:'#E1E1E1', width:ScreenUtils.scaleSize(14), height:ScreenUtils.scaleSize(14)}}//自定义不是当前圆点的样式
                        activeDotStyle={{backgroundColor:'#F6D574', width:ScreenUtils.scaleSize(14), height:ScreenUtils.scaleSize(14)}}//自定义当前页面圆点的样式
                    >
                        <TouchableWithoutFeedback
                            onPress={()=>this.props.navigation.navigate('WebPage', {url: this.state.url1, ...this.props})}
                        >
                            <Image resizemode={'contain'} style={styles.image} source={{uri:this.state.img1}}/>
                        </TouchableWithoutFeedback>
                        <TouchableWithoutFeedback
                            onPress={()=>this.props.navigation.navigate('WebPage', {url: this.state.url2, ...this.props})}
                        >
                            <Image resizemode={'contain'} style={styles.image} source={{uri:this.state.img2}}/>
                        </TouchableWithoutFeedback>
                    </Swiper>

5.文字竖向轮播,跑马灯

实现方法:用的是别人封的一个类,然后根据自己的需要写一下就行了

实现代码:先把下面这个ScrollVertical放在自己项目里面去,然后我是在我的scroll方法里面使用它的

import React, {Component} from 'react'
import {
    Text,
    View,
    Animated,
    Easing,
    StyleSheet,
} from 'react-native'

//这个类直接复制进去就好
export default class ScrollVertical extends Component {
    static defaultProps = {
        enableAnimation: true,
    };

    constructor(props) {
        super(props);
        let translateValue= new Animated.ValueXY({x: 0, y: 0});
        translateValue.addListener(({x,y})=>{
            // Log('value',x,y)
        });
        this.state = {
            translateValue: translateValue,
            // 滚屏高度
            scrollHeight: this.props.scrollHeight || 32,
            // 滚屏内容
            kb_content: [],
            // Animated.View 滚动到的 y轴坐标
            kb_tempValue: 0,
            // 最大偏移量
            kb_contentOffsetY: 0,
            // 每一次滚动切换之前延迟的时间
            delay: this.props.delay || 500,
            // 每一次滚动切换的持续时间
            duration: this.props.duration || 500,
            enableAnimation: true,
        }
    }

    render() {
        return (
            <View style={[styles.kbContainer, {height: this.state.scrollHeight}, this.props.kbContainer]}>
                {
                    this.state.kb_content.length !== 0 ?
                        <Animated.View
                            style={[
                                {flexDirection: 'column'},
                                {
                                    transform: [
                                        {translateY: this.state.translateValue.y}
                                    ]
                                }
                            ]}>
                            {this.state.kb_content.map(this._createKbItem.bind(this))}
                        </Animated.View> : null
                }
            </View>
        )
    }

    componentWillReceiveProps(nextProps) {
        this.setState({
                enableAnimation: nextProps.enableAnimation?true:false
            }, () => {
                this.startAnimation();
            }
        )
    }

    componentDidMount() {
        let content = this.props.data || [];
        console.log("========================");
        console.log(content);
        if (content.length !== 0) {
            let h = (content.length + 1) * this.state.scrollHeight;
            this.setState({
                kb_content: content.concat(content[0]),
                kb_contentOffsetY: h
            });

            // 开始动画
            // this._startAnimation()
            this.startAnimation();
        }
    }


    _createKbItem(kbItem, index) {
        return (
            <View key={index}
                  style={[{ justifyContent: 'center', height: this.state.scrollHeight}, this.props.scrollStyle]}>
                <Text style={[styles.kb_text_c, this.props.textStyle]}>{kbItem.content}</Text>
            </View>
        )
    }

    startAnimation = () => {
        if (this.state.enableAnimation) {
            if(!this.animation){
                this.animation = setTimeout(() => {
                    this.animation=null;
                    this._startAnimation();
                }, this.state.delay);
            }
        }
    };

    componentWillUnmount() {
        if (this.animation) {
            clearTimeout(this.animation);
        }
        if(this.state.translateValue){
            this.state.translateValue.removeAllListeners();
        }
    }

    _startAnimation = () => {
        this.state.kb_tempValue -= this.state.scrollHeight;
        if (this.props.onChange) {
            let index = Math.abs(this.state.kb_tempValue) / (this.state.scrollHeight);
            this.props.onChange(index<this.state.kb_content.length-1?index:0);
        }
        Animated.sequence([

            // Animated.delay(this.state.delay),
            Animated.timing(
                this.state.translateValue,
                {
                    isInteraction: false,
                    toValue: {x: 0, y: this.state.kb_tempValue},
                    duration: this.state.duration, // 动画持续的时间(单位是毫秒),默认为500
                    easing: Easing.linear
                }
            ),
        ])
            .start(() => {
                if (this.state.kb_tempValue - this.state.scrollHeight === -this.state.kb_contentOffsetY) {
                    // 快速拉回到初始状态
                    this.state.translateValue.setValue({x: 0, y: 0});
                    this.state.kb_tempValue = 0;
                }
                this.startAnimation();



            })
    }
}

const styles = StyleSheet.create({
    kbContainer: {
        // 必须要有一个背景或者一个border,否则本身高度将不起作用
        backgroundColor: 'transparent',
        overflow: 'hidden'
    },
    kb_text_c: {
        fontSize: 18,
        color: '#181818',
    }
});
scroll(){
        let array = [{ content: "" }];
        let dataArray = this.state.kuaibaoArray;//这里是数据源
        if (dataArray && dataArray.length > 0) {
            array = [];
            for (let item of dataArray) {
                array.push({ content: item.text});
            }
        }
        if(array[0].content !== ""&&array.length>=1){
            return<ScrollVertical
                onChange={(index => {
                    Index = index;//这个Index是个全局的
                })}
                enableAnimation={true}
                data={array}//放入数据
                delay={2500}
                duration={1000}
                scrollHeight={ScreenUtils.scaleSize(45)}
                scrollStyle={{ alignItems: 'flex-start' }}
                textStyle={{ color: 'black', fontSize:ScreenUtils.setSpText(13) }} />
        }
    }

6.FlatList上拉加载更多(及onEndReached触发时间不对的解决)

实现方法:对onEndReached进行监听,请求时附上页数来展示更多数据

首先实现上拉加载会出现的问题就是onEndReached不正常地调用,所以我们就要对调用这个方法的时机做一下限制,我在网上找了很久没发现解决办法,最后在github上facebook RN的关于Flatlist的issue里面发现了解决方法,可能这个解决方法不是最好的,但目前看来还是勉强能用,如果有更好的方法欢迎交流,下面上代码。

实现代码:

<FlatList
                    renderItem={this.ViewList}
                    data={this.state.dataArray}
                    ListFooterComponent={this._renderFooter.bind(this)}
                    onEndReached={()=>this._onEndReached()}//这里是滑到底执行的方法
                    onEndReachedThreshold={0.1}//这个的值是0到1,网上很多文章都用错了,但如果是我用错了欢迎指正
                    ListHeaderComponent={this._header}
                    refreshing={this.state.Refresh}
                    keyExtractor={this._keyExtractor}
                    //就是通过下面这几个手势监听来判定onEndReached的调用
                    onScrollBeginDrag={() => {
                        this.canAction = true;
                    }}
                    onScrollEndDrag={() => {
                        this.canAction = false;
                    }}
                    onMomentumScrollBegin={() => {
                        this.canAction = true;
                    }}
                    onMomentumScrollEnd={() => {
                        this.canAction = false;
                    }}
                />
_onEndReached(){
        if(!this.canAction) return;//这里就直接拦截了不正确调用onEndReached的情况
        if (this.state.showFoot !== 0){
            return;
        }
        if ((this.state.currentPage!==1)&&(this.state.currentPage>=this.state.totalPage)){
            return;
        }else {
            this.setState({
                currentPage:this.state.currentPage+1
            })
        }
        this.setState({showFoot:2});
        this.onLoad(this.state.currentPage+1);//这里请求更多数据

    }
//根据showFoot的值来判断底部显示的是什么
_renderFooter(){
        if (this.state.showFoot === 1) {
            return (
                <View style={{height:30,alignItems:'center',justifyContent:'flex-start'}}>
                    <Text style={{color:'#999999',fontSize:14,marginTop:5,marginBottom:5,}}> 没有更多数据了 </Text>
                </View>
            );
        }
        else if(this.state.showFoot === 2) {
            return (
                <View style={styles.loading}>
                    <ActivityIndicator/>
                    <Text>Loading...</Text>
                </View>
            );
        } else if(this.state.showFoot === 0){
            return (
                <View style={styles.footer}>
                    <Text/>
                </View>
            );
        }
    }
                //这部分代码是在请求数据的时候的
                let foot = 0;
                if (this.state.currentPage>=page){//这个page是根据需要的分页自己算出来的
                    foot = 1
                }
                this.setState({
                    dataArray: this.state.dataArray.concat(datas),//这里有个小细节需要注意下,就是拼接数据用的是concat来拼接的
                    showFoot:foot,
                });

7.浮动弹窗(Modal的使用):

实现方法:用Modal来自定义浮动弹窗,根据需要来确定Modal的显示状态

实现代码:

<Modal
                    visible={this.state.visible}//可见性
                    animationType='slide'//从下往上滑出
                    transparent={true}
                    onRequestClose={() => this.setModalVisible(false)}>
                    <View style={styles.modalStyle}>
                        //这里自定义Modal的样式
                    </View>
                </Modal>
//在需要显示这个Modal的时候,调用下面这个方法设置true就行了
setModalVisible(visible) {
        this.setState({
            visible: visible
        })
    }

先写这么多吧,有空了再继续

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值