自定义View——React-Native轮播图(学习)

我尽量不打错别字,用词准确,不造成阅读障碍。

最近一直在写RN,赶项目,好久没写博客和更新github了,加上昨天被一个json不会写、甩锅超强的菜鸡同事气到了,今天怎么也得写一些,哭~~。

很多人需要轮播图都会使用开源的"react-native-swiper"之类的东西,人家写的确实牛逼,但是我有个执念,不习惯随意使用第三方的东西,就喜欢造轮子,我一般选择把别人的原理看明白然后自己写,这样可以像做衣服一样,量身定制。所以本文也只是希望可以给需要帮助的人一些实现的思路或者可以学习的地方并顺道记录我的学习状态。

声明:我是Android原生开发出身,并不是前端出身,所以代码风格、js使用技巧等肯定不会很专业,难免会有"low"的地方,见谅见谅,笑~~。

先看效果:
RN_swiper

哈哈,这个gif录的区域歪了,我把状态栏透明了,见谅见谅。

这个在Android和IOS平台都可以跑通,亲测,实际也是因为没有使用任何平台特有的属性和api。
原理:超级简单,就是ScrollView定时滚动,再加上页面指示器,就是底下的小白点。这里做的比较粗糙,没有考虑最后一个页面与第一个页面的过渡问题,完全交给了ScrollView自身Api——scrollTo去解决,实际效果看起来还可以,难道Facebook有处理?

 render() {
        //拿到内容的view集合和内容的style
        let {itemDataList, style, itemRootStyle} = this.props;
        let menuViews = [];
        pageCount = itemDataList.length;
        for (let i = 0; i < pageCount; i++) {
            let onePageDate;
            onePageDate = itemDataList[i];
            menuViews.push(<View style={itemRootStyle} key={i}>{onePageDate}</View>);
        }
        return (
            <View
                style={style}>
                <ScrollView
                    ref="scrollView"
                    horizontal={true}
                    showsHorizontalScrollIndicator={false}
                    pagingEnabled={true}
                    onScroll={(e) => this.onScroll(e)}
                >
                    {menuViews}
                </ScrollView>
                //页面指示器
                {this.getPageControl(this.state.currentPage, pageCount)}
            </View>);
    }

其中滚动内容的View集合和内容style是从外面传过来的,用for循环加入到一个数组menuViews中,最后作为ScrollView的数据源,最最后,加入页面指示器。

页面指示器也是我自定义的,先写上调用方法:

getPageControl(currentPage, numberOfPages) {
            let control = (<PageControl
                style={MyCarouseStyle.pageControl}
                numberOfPages={pageCount}
                currentPage={this.state.currentPage}
                indicatorStyle={this.props.indicatorStyle}
                currentIndicatorStyle={this.props.currentIndicatorStyle}
            />);
        return control;
    }

style是样式,nubmerOfPages是页面总数,根据数据源计算传入,currentPage是当前页面值,因为要根据ScrollView滚动来改变,所以我觉得写在外面好处理。indicatorStyle是默认指示器样式,currentIndicatorStyle是当前指示器样式,即选中状态。

贴上指示器代码:

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {View, StyleSheet} from 'react-native'
import screen from "../utils/Screen";

export default class PageControl extends Component {

    constructor(props: Object) {
        super(props);
    }

    static propTypes = {
        numberOfPages: PropTypes.number.isRequired,
        currentPage: PropTypes.number,
        indicatorStyle: PropTypes.object,
        currentIndicatorStyle: PropTypes.object,
        style: PropTypes.object,
    };

    static defaultProps = {
        numberOfPages: 0,
        currentPage: 0,
        indicatorStyle: {backgroundColor: "#000", width: 8, height: 8, borderRadius: 4},
        currentIndicatorStyle: {
            backgroundColor: "#ff0000",
            width: 8,
            height: 8,
            borderRadius: 4
        },
        style: {}
    };

    render() {
        let {indicatorStyle, currentIndicatorStyle, numberOfPages, currentPage, style} = this.props;
        return (
            <View style={style}>
                {this.setItem(numberOfPages, currentPage, currentIndicatorStyle, indicatorStyle)}
            </View>
        )
    }

    setItem(numberOfPages, currentPage, currentIndicatorStyle, indicatorStyle) {
        let menuViews = [];
        for (let i = 0; i < numberOfPages; i++) {
            let menuView = (<View key={i} style={i === currentPage ? currentIndicatorStyle : indicatorStyle}/>);
            menuViews.push(menuView);
        }
        return (<View style={styles.container}>{menuViews}</View>);
    }
}
                
const styles = StyleSheet.create({
    container: {
        backgroundColor: 'transparent',
        alignItems: 'center',
        justifyContent: 'center',
        flexDirection: 'row',
        height: 10,
        width: screen.width,
        textAlign: 'center'
    }
});

很简单的,指示器的代码我贴的是最全的,就不用解释了吧,笑哭~。

再看一下定时器的处理:

 autoPlayInterval = null; //计时器

    componentDidMount() {
        let {isAutoPlay, width} = this.props;
        let page = 0;
        if (isAutoPlay) {
            this.autoPlayInterval = setInterval(() => {
                page = page < pageCount ? page + 1 : 0;
                this.refs.scrollView.scrollTo({x: width * page}, 0, true)
            }, 2000);
        }
    }

//监听滚动事件更新view
 onScroll(e: any) {
        let x = e.nativeEvent.contentOffset.x;
        let currentPage = Math.round(x / screen.width);

        if (this.state.currentPage !== currentPage) {
            this.setState({currentPage: currentPage});
        }
    }
//最后别忘了clear定时器,官方建议~~
componentWillUnmount() {
        this.autoPlayInterval && clearInterval(this.autoPlayInterval);
    }

isAutoPlay表示是否自动轮播。其实就是根据每一个页面的宽度去调用scrollTo函数,因为页面宽度不一定是屏幕宽度啊,谁知道会不会有左右间距25px之类的。

最后看看怎么调用的,毕竟有很多属性是外面传进来的。

 <MyCarouselView style={{}}
                 itemDataList={this.headViewItem2()}
                 itemRootStyle={{}}
                 indicatorStyle={homeStyle.headIndicatorStyle}
                 currentIndicatorStyle={homeStyle.headCurrentIndicatorStyle}
                 isAutoPlay={true}
                 width={screen.width}
                 type=""/>

有些属性比如style、type传个空就好了,因为我这个是从项目中拿出来的,通用性没太考虑。headViewItem2()方法里面是数据源:

headViewItem2() {
        let imageList = [];
        for (let i = 0; i < 4; i++) {
            let itemView = (
                <TouchableOpacity
                    key={i}
                    onPress={() => this.goProduceDetail()}
                >
                    <View style={homeStyle.headView}>
                        <Text style={{
                            textAlign: "center",
                            color: '#000',
                            width: screen.width,
                            paddingTop: 85,
                            height: 180,
                            backgroundColor: '#cc0000'
                        }}>页面{i}</Text>
                    </View>
                </TouchableOpacity>
            );
            imageList.push(itemView);
        }
        return imageList;
    }

举个例子这么写;

本文并没有github或其他Demo地址,因为这是我从项目里抠出来的,很多style和import我没写,这应该不会成为学习的障碍,除非你是比我还白的小白,笑哭~~~~。有问题欢迎提问,我看到就会尽力给你解答。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值