zoukankan      html  css  js  c++  java
  • React Native 开发豆瓣评分(七)首页组件开发

    首页内容拆分

    看效果图,首页由热门影院、豆瓣热门、热门影视等列表组成,每个列表又由头加横向滑动的 电影海报列表构成。

    所以可以先把页面的电影海报、评分、列表头做成组件,然后在使用 ScrollView 将内容包裹即可构成首页。

    开发头部组件

    头部组件结构简单,唯一需要注意的就是点击查看更多的时候需要跳转页面,所有需要一个自定义事件,供页面使用。

    在 src 目录创建 itemsHeader.js,内容如下:

    import { Text, View, StyleSheet, TouchableWithoutFeedback } from 'react-native';
    import PropTypes from 'prop-types';
    import { px } from '../utils/device';
    import Icon from 'react-native-vector-icons/AntDesign';
    
    export default class ItemsHeader extends Component {
        constructor(props) {
            super(props);
        }
        static propTypes = {
            title: PropTypes.string,
            onPress: PropTypes.func
        }
        static defaultProps = {}
        render() {
            const { title, onPress } = this.props;
            return (
                <View style={styles.header}>
                    <Text style={styles.title}>{title}</Text>
                    <TouchableWithoutFeedback onPress={() => onPress && onPress()}>
                        <View style={styles.getMore}>
                            <Text style={styles.moreText}>查看更多</Text>
                            <Icon name='right' size={px(30)} color='#00b600'></Icon>
                        </View>
                    </TouchableWithoutFeedback>
                </View>
            )
        }
    }
    
    const styles = StyleSheet.create({
        header: {
            height: px(90),
             px(750),
            paddingLeft: px(30),
            paddingRight: px(30),
            backgroundColor: '#ffffff',
            flexDirection: 'row',
            alignItems: 'center',
            justifyContent: 'space-between'
        },
        title: {
            fontSize: px(32),
            color: '#333',
            fontWeight: '600'
        },
        getMore: {
            flexDirection: 'row',
            alignItems: 'center',
        },
        moreText: {
            fontSize: px(28),
            marginLeft: px(30),
            color: '#00b600',
            marginRight: px(6)
        }
    });
    

    开发评分组件

    评分组件需要考虑到星星大小、间距、颜色、数量,点击星星时改变星星的选中状态,并返回自定义事件 onPress 供调用者使用。

    如果使用组件时调用了onPress,那么组件的值为可以改变,如果没有,那么组件应该为只读状态。

    import React, { Component } from 'react';
    import { Text, View, StyleSheet } from 'react-native';
    import PropTypes from 'prop-types';
    import { px } from '../utils/device';
    import Icon from 'react-native-vector-icons/AntDesign';
    
    export default class Rate extends Component {
        constructor(props) {
            super(props);
            this.state = {
                value: this.props.value
            }
        }
        componentWillReceiveProps(newProps) {
            const { value } = newProps;
            if (value !== this.state.value) {
                this.setState({
                    value
                });
            }
        }
        static propTypes = {//如果使用组件时调用了onPress,那么组件默认为可以改变,如果没有,那么组件应该为只读
            value: PropTypes.number,
            size: PropTypes.number,
            margin: PropTypes.number,
            max: PropTypes.number,
            color: PropTypes.string,
            onPress: PropTypes.func
        }
        static defaultProps = {
            value: 0,
            size: 20,
            margin: 5,
            max: 5,
            color: '#00b600'
        }
        bindClick = (index) => {
            const { onPress } = this.props;
            if (!onPress) {
                return;
            }
            onPress(index + 1);
            this.setState({
                value: index + 1
            })
        }
        render() {
            const { size, margin, max, color, onPress } = this.props;
            const { value } = this.state;
            const defaultStars = [], activeStars = [];
            for (let i = 0; i < max; i++) {
                defaultStars.push(<Icon name='star' key={i} size={size} color='#ececec' onPress={() => this.bindClick(i)} style={{ marginRight: margin }}></Icon>)
            }
            for (let i = 0; i < value; i++) {
                activeStars.push(<Icon name='star' key={i} size={size} color={color} onPress={() => this.bindClick(i)} style={{ marginRight: margin }}></Icon>)
            }
            // 选中状态的星星的宽度
            const activeStarsWidth = (size + margin) * Math.floor(value) + size * (value - Math.floor(value));
            return (
                <View style={styles.rate}>
                    <View style={[styles.stars, styles.active, {  activeStarsWidth }]}>
                        {activeStars.map(item => item)}
                    </View>
                    <View style={styles.stars}>
                        {defaultStars.map(item => item)}
                    </View>
                </View>
            )
        }
    }
    
    
    const styles = StyleSheet.create({
        rates: {
            flexDirection: 'row',
            position: 'relative'
        },
        stars: {
            flexDirection: 'row',
            alignItems: 'center',
            overflow: 'hidden',
            flexGrow: 0
        },
        active: {
            position: 'absolute',
            zIndex: 200,
            left: 0,
            top: 0
        }
    });
    

    开发电影海报组件

    海报组件开发需要注意的是:

    1. 点击电影海报,跳转详情页面,跳转逻辑都是一样的,所以可以不用自定义事件的方式跳转,直接在组件里面调用 this.props.navigation.push 进行跳转。页面在 router 里注册后可以直接使用 this.props.navigation.push,但是组件不行。在组件中,想要使用 navigation 进行跳转,要么是使用自定义属性,将 navigation 传入组件,要么使用 react-navigation 提供的 withNavigation翻翻,withNavigation(component) 返回一个 render 函数,默认将 navigation 作出自定义属性传入组件。

    2. 有些海报图片背景纯白,和页面背景融合了,看不到边界,所以需要给他设置 border,由于 Image 组件不能设置 border,所以这里需要使用 ImageBackground 组件。

    3. title 只能为一行,产出部分省略,需要加一个 numberOfLines={1} 的属性。

    import React, { Component } from 'react';
    import { Text, View, StyleSheet, ImageBackground, TouchableWithoutFeedback } from 'react-native';
    import PropTypes from 'prop-types';
    import { withNavigation } from 'react-navigation';
    import { px } from '../utils/device';
    import Rate from './rate';
    
    class MoviesItem extends Component {
        constructor(props) {
            super(props);
        }
        static propTypes = {
            data: PropTypes.object
        }
        render() {
            const { data, navigation } = this.props;
            const { id, title, cover, rating, null_rating_reason } = data;
            return (
                <TouchableWithoutFeedback onPress={() => navigation.push('Detail', { id })}>
                    <View style={styles.page}>
                        <ImageBackground source={{ uri: cover.url }} style={styles.img}></ImageBackground>
                        <Text style={styles.title} numberOfLines={1}>{title}</Text>
                        {rating ? (
                            <View style={styles.rate}>
                                <Rate value={rating.value / 2} size={px(20)} margin={px(4)} />
                                <Text style={styles.rateText}>{rating.value.toFixed(1)}</Text>
                            </View>
                        ) : (
                            <Text style={styles.rate}>{null_rating_reason}</Text>
                        )}
                    </View>
                </TouchableWithoutFeedback>
            )
        }
    }
    
    export default withNavigation(MoviesItem);
    
    const styles = StyleSheet.create({
        page: {
             px(160)
        },
        img: {
             px(160),
            height: px(224),
            overflow: 'hidden',
            borderRadius: px(8),
            borderWidth: 1,
            borderStyle: 'solid',
            borderColor: '#f8f8f8'
        },
        title: {
            fontSize: px(28),
            fontWeight: '600',
            color: '#333',
            marginTop: px(12),
            lineHeight: px(40)
        },
        rate: {
            flexDirection: 'row',
            alignItems: 'center'
        },
        rateText: {
            fontSize: px(24),
            color: '#999',
            marginLeft: px(6)
        }
    });
    

    使用

  • 相关阅读:
    Git使用(真的有熟悉吗)
    webpack中webpack.config.js的相关配置表
    import和require(你有过疑惑么)
    webpack打包(离开脚手架,你还好吗)
    webpack再度学习
    最大子段和之分治法
    快速排序之随机快排
    棋盘覆盖问题(看完这个就好)
    稳定匹配问题(脱单就靠这波了)
    Jieba统计词频并生成csv文件
  • 原文地址:https://www.cnblogs.com/hl1223/p/11139884.html
Copyright © 2011-2022 走看看