React Native開發(fā)星級評價組件,展示和選擇(帶思路),拿來就可以用

整體思路[根據(jù)自己的Angular4+開發(fā)經(jīng)驗而來,可能我寫的代碼有點偏向于Angular,不過思路更清晰, 同時我會把思路說的非常清楚, 我時刻明白, 如果你不能給一個三歲小孩子講清楚,那么你就是不會.]

以下的思路, 可以用到任何框架上面, 同時用了width做了屏幕適配, 不會對原有項目代碼造成任何影響, 請放心使用

比如Angular中 把ListView當成一個div, 把渲染換成*ngFor, 數(shù)據(jù)放到 constructor函數(shù)里面返回
比如 Vue 中, 把ListView當成一個div, 把渲染換成v-for, 數(shù)據(jù)放到 data函數(shù)里面返回,
比如 React 中, 把ListView當成一個div,直接用數(shù)組的一些遍歷函數(shù)map一類都可以渲染出來組件, React和下面的代碼差不多
星級評價組件預(yù)覽


星級評價組件預(yù)覽

如果你不想畫上3分鐘看思路, 那么最下面有代碼可以直接用,

  • 星級展示思路
    傳入值, 1-5 任意數(shù)字
    然后把把數(shù)字組成數(shù)組, 舉例 假如傳入的是4,那么會有一個這樣的數(shù)組,也就是把值都變?yōu)閠rue
[true, true, true, true, false] 

那么就可以根據(jù)這里面的值,改變組件的類名,從而改變了樣式

具體實現(xiàn)

  1. 在constructor拿到傳入的級別,類型是數(shù)字類型 int
  constructor(props) {
        super(props);
       // @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
        this.rating = this.props.rating <= 5 && this.props.rating >= 0 ? this.props.rating : 5
}
  1. 導(dǎo)入RN組件,同時創(chuàng)建一個變量用來保存,ListView將要遍歷的值
// 導(dǎo)入ListView
  import { ListView } from "react-native";
  constructor(props) {
        super(props);
        // @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
        this.rating = this.props.rating <= 5 && this.props.rating >= 0 ? this.props.rating : 5
        // @param {是否可編輯,如果不傳,默認是不可編輯,值為false} this.props.editable
        this.editable = this.props.editable ? this.props.editable : false
        // 定義準備被ListView包裝的數(shù)組    this.ratingArr: Array<object> =[]
        this.ratingArr = []
       // 定義并聲明 `ListView` 的包裝函數(shù)  this.dSource: any
        this.dSource = (new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 })) 
       // 定義被ListView的包裝函數(shù)  this.dSource處理的數(shù)據(jù),這個數(shù)據(jù)是用來渲染使用的
        this.state = { rating: this.dSource.cloneWithRows(this.ratingArr) }
    }
  1. 處理this.ratingArr的數(shù)據(jù)結(jié)構(gòu)
      setRatingChange = () => {
        // 這里的星級是5級,如果你是其他星級,可以更改,
        this.ratingArr = Array.from({ length: 5 }).map((item, index) => {
        // 這句話的是,如果傳入的等級小于或者等于index+1,
        // 那么才返回一個true, true代表的是點亮星星個數(shù)
            if (this.rating <= index + 1) {
                return {
                    status: true,
                    index,
                    // 索引都是0開始的, 因此需要增加1 
                    level: index + 1
                }
            } else {
                return {
                    status: false,
                    index,
                    level: index + 1
                }
            }
        })
    }
  1. this.state.rating 渲染到ListView中,特別強調(diào)[兩點]
    • ListView的屬性contentContainerStyle是用來寫ListView樣式的,其中,為了防止各種版本之間的缺陷,一點要寫上樣式flexWrap: "wrap"
    • RN的0.28版本的時候, 改變了內(nèi)部代碼, 從而,造成了, 需要定義即將渲染的元素的 alignItems"flex-start"
 render = () => {
        return (<View style={styles.vView}>
            <View style={[styles.vRatingV, styles.vRating_s]}>
                <ListView
                    contentContainerStyle={styles.vRating}
                    dataSource={this.state.rating}
                   // 這里是根據(jù)數(shù)組里的是否是true進行渲染不同的樣式,
                   // 用了箭頭函數(shù), 代表直接return一個組件,采用的是JSX語法
                    renderRow={(item, index) => <Text
                    style={[styles.rating, this.rating < item.level ? styles.rating_n : ""]}>&#xe61d;</Text>}
                />
            </View>
        </View>)
    }

以上就是展示的代碼完成了,

  • 星級選擇思路
  1. 星級選擇的思路也簡單, 通過上面的星級展示思路, 想必也清楚了一個核心就是根據(jù)傳入的星級的等級生成一個含有true,false的值
    因此,我們可以根據(jù)選擇的是哪個星星, 根據(jù)該星星的索引對數(shù)組this.ratingArr = []重新計算值, 然后把ListView的dataSource的值進行重新賦值
  2. 我們增加一個點擊函數(shù)事件
 render = () => {
        return (<View style={styles.vView}>
            <View style={[styles.vRatingV, styles.vRating_s]}>
                <ListView
                    contentContainerStyle={styles.vRating}
                    dataSource={this.state.rating}
                    renderRow={(item, index) => <Text
                   // 這里增加一個點擊事件`this.selectStar`
                   // 參數(shù)是`item`和`index`, 用來進行選擇
                    onPress={this.selectStar.bind(this, item, index)}
                    style={[styles.rating, this.rating < item.level ? styles.rating_n : ""]}>&#xe61d;</Text>}
                />
            </View>
        </View>)
    }

這里是添加的點擊事件匿名變量函數(shù)

    selectStar = (item, Windex) => {
        this.rating = item.level
        this.setRatingChange()
        this.setState({ rating: this.dSource.cloneWithRows(this.ratingArr) })
    }
  1. 以上就完成了點擊切換功能

怎么封裝一個組件出去呢?

我們可以定義三個導(dǎo)入值, 為了防止報錯,(比如沒有傳入值等), 我們需要定義三個具體的參數(shù),用來說明,分別是
/**

  • @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
  • @param {是否可編輯,如果不傳,默認是不可編輯,值為false} this.props.editable
  • @param {接收的函數(shù),如果不傳遞,那么不會傳遞出去,只會什么都不做} this.props.selectStar
    */
  1. 我們需要在初始化的時候處理這參數(shù), 其中第三個參數(shù)this.props.selectStar可以放到使用的時候再判斷,
        // @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
        this.rating = this.props.rating <= 5 && this.props.rating >= 0 ? this.props.rating : 5
        // @param {是否可編輯,如果不傳,默認是不可編輯,值為false} this.props.editable
        this.editable = this.props.editable ? this.props.editable : false
  1. 對是否能都編輯,進行判斷, 如果傳入了可以編輯,那么就不會使用
    this.editable的默認值
    同時,我們可以改造一下點擊事件函數(shù)
    selectStar = (item, Windex) => {
        // 判斷是否傳入了可編輯
        if (this.editable == true) {
            this.rating = item.level
            this.setRatingChange()
            this.setState({ rating: this.dSource.cloneWithRows(this.ratingArr) })
            // 把值傳遞出去,如果為真,那么可以作為參數(shù)傳遞出去,如果為假,則無法傳遞出去
            this.props.selectStar ? this.props.selectStar(item.level) : ""
            return item.level
        } else {
            return false
        }
    }
  1. 我們可以定義一個最大星級
  • @param {最大星級,如果不傳,默認是5,值為5} this.props.maxRating
  1. 如果父組件傳入的是一個變量,或者用了很多的引用, 那么需要在子組件里面實時更改值,而不是第一次的時候就默認那樣子了
   componentWillReceiveProps = (nextProps) => {
        this.maxRating = nextProps.maxRating ? nextProps.maxRating : 5
        this.rating = nextProps.rating ? (nextProps.rating >= 0 && nextProps.rating <= this.maxRating ? nextProps.rating : this.maxRating) : this.maxRating
        this.editable = nextProps.editable ? nextProps.editable : false
        this.props.selectStar = nextProps.selectStar ? nextProps.selectStar : null
        this.initPage()
    }

我們最后封裝封裝代碼優(yōu)化一下,這樣子的代碼看著才更簡潔, 下面附代碼,

使用的使用,直接

    import Rating from "./rating"
.....N多代碼
    render = () =>{
        return (<View>
                        <Rating rating={this.state.companyDetail.rating ? this.state.companyDetail.rating / 10 : 5}></Rating>
                </View>)
        }
    

github源碼

/*
 * @Description: 星級評論組件
 * @version: 0.1.0
 * @Company: 
 * @Author: AmandaYi
 * @Date: 2018-10-25
 * @LastEditors: AmandaYi
 * @LastEditTime: 2018-10-25 
 */
/**
 * @param {最大星級,如果不傳,默認是5,值為5} this.props.maxRating
 * @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
 * @param {是否可編輯,如果不傳,默認是不可編輯,值為false} this.props.editable
 * @param {接收的函數(shù),如果不傳遞,那么不會傳遞出去,只會什么都不做} this.props.selectStar
 */
import React, { Component } from "react";
import { View, Text, StyleSheet, ListView } from "react-native";
import globalStyle, {
    width,
    rx
} from "./variable"
const styles = StyleSheet.create({
    vView: {
        position: "relative"
    },
    vRating: {
        flexDirection: "row",
        flexWrap: "wrap",
        width,
    },
    vRatingV: {
        position: "absolute",
        top: 0,
        left: 0,
    },
    vRating_s: {
        zIndex: 1000
    },
    vRating_n: {
        zIndex: 800
    },
    rating: {
        fontFamily: "iconfont",
        fontSize: rx(34),
        color: '#ff6600',
        marginRight: rx(10),
        backgroundColor: "#ffffff",
        alignItems: "flex-start",

    },
    rating_n: {
        color: '#cccccc',
    }
})
export default class Rating extends Component {
    constructor(props) {
        super(props);
        // 定義總星級
        this.maxRating = this.props.maxRating ? this.props.maxRating : 5
        // @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
        this.rating = this.props.rating <= this.maxRating && this.props.rating >= 0 ? this.props.rating : this.maxRating
        // @param {是否可編輯,如果不傳,默認是不可編輯,值為false} this.props.editable
        this.editable = this.props.editable ? this.props.editable : false
        this.ratingArr = []
        this.dSource = (new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 }))
        this.initPage()
        this.state = { rating: this.dSource.cloneWithRows(this.ratingArr) }
        this.props.selectStar = null
    }
    selectStar = (item, Windex) => {
        // 判斷是否傳入了可編輯
        if (this.editable == true) {
            this.rating = item.level
            this.setRatingChange()
            this.setState({ rating: this.dSource.cloneWithRows(this.ratingArr) })
            // 把值傳遞出去,如果為真,那么可以作為參數(shù)傳遞出去,如果為假,則無法傳遞出去
            this.props.selectStar ? this.props.selectStar(item.level) : ""
            return item.level
        } else {
            return false
        }
    }
    componentWillReceiveProps = (nextProps) => {
        this.maxRating = nextProps.maxRating ? nextProps.maxRating : 5
        this.rating = nextProps.rating ? (nextProps.rating >= 0 && nextProps.rating <= this.maxRating ? nextProps.rating : this.maxRating) : this.maxRating
        this.editable = nextProps.editable ? nextProps.editable : false
        this.props.selectStar = nextProps.selectStar ? nextProps.selectStar : null
        this.initPage()
    }
    // 處理函數(shù)
    initPage = () => {
        this.setRatingChange()
    }
    // 改變狀態(tài)
    setRatingChange = () => {
        this.ratingArr = Array.from({ length: this.maxRating }).map((item, index) => {
            if (this.rating <= index + 1) {
                return {
                    status: true,
                    index,
                    level: index + 1
                }
            } else {
                return {
                    status: false,
                    index,
                    level: index + 1
                }
            }
        })
    }
    render = () => {
        return (<View style={styles.vView}>
            <View style={[styles.vRatingV, styles.vRating_s]}>
                <ListView
                    contentContainerStyle={styles.vRating}
                    dataSource={this.state.rating}
                    renderRow={(item, index) => <Text
                        onPress={this.selectStar.bind(this, item, index)}
                        style={[styles.rating, this.rating < item.level ? styles.rating_n : ""]}>&#xe61d;</Text>}
                />
            </View>
        </View>)
    }
}

注意點, 一定要更改適配文件的UI設(shè)計稿的寬度,默認是750,具體可以看代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 作為一個合格的開發(fā)者,不要只滿足于編寫了可以運行的代碼。而要了解代碼背后的工作原理;不要只滿足于自己的程序...
    六個周閱讀 8,686評論 1 33
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,351評論 25 708
  • 全球變暖,夏天越發(fā)燥熱,我居住著的古都長安突破了40°的高溫,一出超市樹下的風都帶著黏膩膩的汗味,真想對著太陽來...
    小北尾閱讀 1,009評論 0 0
  • 電瓶車和方便面,是最近微博上的兩個熱點。從評論看,大家都很高大上,感覺不是一個世界。 先說方便面,因為一個爭吵視頻...
    花椒樹閱讀 525評論 0 0
  • 偶爾 想起了你,不知 你現(xiàn)在是否還安好 昨天的日子 如云煙 遮擋了望眼 雖然時間過去了那么久 但是你依然是我 沒有...
    利君理療閱讀 109評論 0 0

友情鏈接更多精彩內(nèi)容