整體思路[根據(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)
- 在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
}
- 導(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) }
}
- 處理
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
}
}
})
}
- 把
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 : ""]}></Text>}
/>
</View>
</View>)
}
以上就是展示的代碼完成了,
- 星級選擇思路
- 星級選擇的思路也簡單, 通過上面的星級展示思路, 想必也清楚了一個核心就是根據(jù)傳入的星級的等級生成一個含有true,false的值
因此,我們可以根據(jù)選擇的是哪個星星, 根據(jù)該星星的索引對數(shù)組this.ratingArr = []重新計算值, 然后把ListView的dataSource的值進行重新賦值 - 我們增加一個點擊函數(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 : ""]}></Text>}
/>
</View>
</View>)
}
這里是添加的點擊事件匿名變量函數(shù)
selectStar = (item, Windex) => {
this.rating = item.level
this.setRatingChange()
this.setState({ rating: this.dSource.cloneWithRows(this.ratingArr) })
}
- 以上就完成了點擊切換功能
怎么封裝一個組件出去呢?
我們可以定義三個導(dǎo)入值, 為了防止報錯,(比如沒有傳入值等), 我們需要定義三個具體的參數(shù),用來說明,分別是
/**
- @param {星級,如果不傳,默認是滿級,值為5} this.props.rating
- @param {是否可編輯,如果不傳,默認是不可編輯,值為false} this.props.editable
- @param {接收的函數(shù),如果不傳遞,那么不會傳遞出去,只會什么都不做} this.props.selectStar
*/
- 我們需要在初始化的時候處理這參數(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
- 對是否能都編輯,進行判斷, 如果傳入了可以編輯,那么就不會使用
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
}
}
- 我們可以定義一個最大星級
- @param {最大星級,如果不傳,默認是5,值為5} this.props.maxRating
- 如果父組件傳入的是一個變量,或者用了很多的引用, 那么需要在子組件里面實時更改值,而不是第一次的時候就默認那樣子了
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>)
}
/*
* @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 : ""]}></Text>}
/>
</View>
</View>)
}
}