赞
踩
开始学习React Native也有5天了,在项目时发现RN没有给提供RadioButton和RadioGroup这两个组件,只有CheckBox组件,但是项目中确实有好处需要使用到RadioButton和RadioGroup,既然没有,那就自己写吧。
效果图:
需求分析:
1)可以指定选中状态和未选中状态的图片、图片的大小:selectedImg、unSelectedImg、imgSize
2)可以指定选中状态和未选中状态的文字、文字大小、文字颜色 text,textSize,selectedTextColor,unSelectedTextColor
3)可以指定文字距离图片的距离:drawablePadding
4)可以指定整体RadioButton的间距,就像使用系统组件一样,这个用style来接收
5)如果使用RadioGroup来创建RadioButton,还可以设置不同的RadioButton是横向排列还是纵向排列,如果是横向排列那么2个RadioButton之间的间距是多少,如果是纵向排列,那么纵向的间距又是多少,这些我们用2个变量来控制:oritation和margin,oritation用来控制横纵向的排列,margin代表2个Button之间的距离。
需求有了之后,其实写起来很简单,无非就是一张图片和文字再加一些样式而已,在RN中控制一个View动态改变需要使用到state,这里定义一个state变量selected来记录RadioButton是否被选中,为true为选中状态,false为未选中状态:
- state = {
- // 让当前的变量为传递过来的是否选中的状态,在使用RadioButton时是可以指定属性selected,在这里接收
- selected: this.props.selected,
- };
state变量和动态控制一个组件的改变,但是组件改变之前仍然可能会显示一些东西,这些东西用props来控制,而且可以用这些props定义一些默认的属性,例如我们可以定义默认的文字颜色等:
- // 默认属性
- static defaultProps = {
- selectedChanged: false,
- selectedTextColor: '#333333',
- unSelectedTextColor: '#333333',
- };
下面就是在render方法中渲染图片和文字,以及控制一些style属性,首先获取到传递过来的各种属性值以及当前的状态值:
- const {
- selectedImg, unSelectedImg, imgSize, text, selectedTextColor, oritation,
- unSelectedTextColor, textSize, drawablePadding, margin, style,
- } = this.props;
- const {selected} = this.state;
因为在渲染时多个不同的RadioButton排列方向可以是横纵方向,所以这个时候就需要注意传递过来的margin是marginLeft还是marginTop,所以先定义一个变量style,根据方向的不同,来决定到底是marginLeft还是marginTop:
- let marginStyle = {};
- if (oritation === 'row') {
- marginStyle = {
- flexDirection: 'row',
- alignItems: 'center',
- marginLeft: margin,
- }
- } else {
- marginStyle = {
- flexDirection: 'row',
- alignItems: 'center',
- marginTop: margin,
- }
- }
然后返回一个整体的View即可:这里需要注意,在使用时我们可能会给这个RadioButton添加style属性,例如marginLeft,marginTop等,这个是在外面设置的,在内部我们同样会设置style属性,这个style是用来控制图片和文字这2个元素为横向排列,所以需要把外面传递过来的属性和内部的属性合到一起,多个属性使用数组就行,例如下面的:
style={[marginStyle, style]},然后就是根据state以及传入过来的属性设置具体的值。
- return (
- <View style={[marginStyle, style]}>
- <Image
- style={{width: imgSize, height: imgSize}}
- source={selected ? selectedImg : unSelectedImg}/>
- <Text style={{
- color: selected ? selectedTextColor : unSelectedTextColor,
- fontSize: textSize, marginLeft: drawablePadding
- }}>{text}
- </Text>
- </View>
- );
这样就可以自定义图片、文字等属性了,有了这些之后,还需要一个点击事件来改变样式的显示,并且提供给外面一个监听,让外面能够知道改变了,首先声明一个成员变量代表监听事件:
selectedChanged;
然后在构造方法中给这个变量赋值:
- constructor(props) {
- super(props);
- this.selectedChanged = props.selectedChanged;
- }
然后给最外层的View添加TouchableOpacity组件,添加点击事件:
- return (
- <TouchableOpacity onPress={() => {
- if (this.selectedChanged) { // 这个是判断外界是否传入了这个监听事件,如果没有传入,则直接调用this.selectedChanged会出错
- // this.selectedChanged就是调用了我们刚才生命的成员变量,可以代表一个方法,这里传入一个现在的选中状态的值和上一次状态值
- this.selectedChanged(selected, !selected);
- }
- // 点击之后改变state值,该值一旦改变之后就会重新调用render方法进行绘制
- this.setState({
- selected: !selected,
- })
- }}>
- <View
- ...
- </View>
- </TouchableOpacity>
- );
在外界使用:
- <RadioButton
- selected={true}
- selectedImg={Images.InvoiceInfo.RadioButtonSelected}
- unSelectedImg={Images.InvoiceInfo.RadioButtonUnSelected}
- imgSize={20}
- text='个人'
- textSize={12}
- drawablePadding={8}
- selectedChanged={(oldState, newState) => {
- console.log(oldState + "--" + newState);
- }}/>
使用RadioButton大部分情况是多个共同使用,而且只能有一个被选中,android中就有这样的组件,但是在RN中没有找到,其实这个也很容易实现,原理是通过RadioGroup来生成多个RadioButton并且持有这些RadioButton的引用,当一个被选中的时候把其他的置为不选中,使用RadioGroup时给这个RadioGroup传递一个数组即可,然后RadioGroup通过数组来创建RadioGroup,因为同样要指定RadioButton的样式,所以在外部使用时直接把style的各种样式和属性一并传递给RadioGroup,RadioGroup在创建RadioButton时把这些样式属性再传递给RadioButton即可,原理很简单,直接上代码:
- import React, {Component} from 'react';
- import {
- View,
- } from 'react-native';
- import RadioButton from "./RadioButton";
-
- /**
- * RadioGroup:
- * 里面只能存放RadioButton,生成多少个RadioButton,需要传递data数组,不管生成多少个RadioButton,只能有一个被选中
-
- 使用示例:
- <RadioGroup
- style={{marginLeft: 20, marginTop: 23}} // 指定RadioGroup的样式
- oritation='column' // 里面RadioButton是横向排列还是纵向排列:'column','row'
- margin={15} // 如果oritation='column',则代表多个RadioButton横向间距,为'row',则代表多个RadioButton纵向间距
- data={[{"text": "个人"}, {"text": "单位"}, {"text": "其他"}]} // data需要传递一个数组
- selectedImg={Images.InvoiceInfo.RadioButtonSelected} // 被选状态时的图片
- unSelectedImg={Images.InvoiceInfo.RadioButtonUnSelected} // 未被选状态时的图片
- imgSize={20} // 图片的大小(宽高一样)
- text='单位' // 文字
- textSize={12} // 文字大小
- drawablePadding={8} // 文字和图片之间的距离
- itemChange={(index) => { // 某一个item被选中时的事件监听,会返回当前选中的item的索引位置
- ToastAndroid.show("" + index, ToastAndroid.SHORT);
- }}
- />
- */
- export default class RadioGroup extends Component {
-
- // 当前选中的item索引
- currentIndex = -1;
- // 存放所有的RadioButton的引用
- referencesArray = [];
- // 选中某一个item的回调监听
- itemChange;
-
- constructor(props) {
- super(props);
- this.itemChange = props.itemChange;
- }
-
- render() {
- // 清空数组,避免多次render重复向数组中添加
- this.referencesArray = [];
- const {
- data, selectedImg, unSelectedImg, imgSize, selectedTextColor, oritation,
- unSelectedTextColor, textSize, drawablePadding, margin, style,
- } = this.props;
- return (
- <View style={[style, {flexDirection: oritation}]}>
- {
- data.map((radioData, index) => {
- return (
- <RadioButton
- key={index}
- ref={radioButton => this.referencesArray.push(radioButton)}
- text={radioData.text}
- selectedImg={selectedImg}
- unSelectedImg={unSelectedImg}
- imgSize={imgSize}
- selectedTextColor={selectedTextColor}
- unSelectedTextColor={unSelectedTextColor}
- textSize={textSize}
- oritation={oritation}
- drawablePadding={drawablePadding}
- margin={index === 0 ? null : margin}
- selectedChanged={() => {
- this.change(index);
- }}
- />
- );
- })
- }
- </View>
- );
- }
-
- /**
- * 某一个item选中后的事件
- * @param index 当前选中的item的索引
- */
- change(index) {
- this.currentIndex = index;
- // 遍历引用数组,通知每一个RadioButton改变状态
- this.referencesArray.map((refer, index2) => {
- if (refer !== null) {
- refer.setSelectedState(index2 === this.currentIndex);
- }
- });
- // 调用回调监听
- this.itemChange(this.currentIndex);
- }
- }
把RadioButton的全部代码一起附上:
- import React, {Component} from 'react';
- import {
- Text,
- View,
- TouchableOpacity,
- Image,
- } from 'react-native';
- import Images from "../Images";
-
- /**
- *
- <RadioButton
- selected={true}
- selectedImg={Images.InvoiceInfo.RadioButtonSelected}
- unSelectedImg={Images.InvoiceInfo.RadioButtonUnSelected}
- imgSize={20}
- text='个人'
-
- textSize={12}
- drawablePadding={8}
- selectedChanged={(oldState, newState) => {
- console.log(oldState + "--" + newState);
- }}/>
- */
- export default class RadioButton extends Component {
-
- selectedChanged;
-
- constructor(props) {
- super(props);
- this.selectedChanged = props.selectedChanged;
- }
-
- state = {
- selected: this.props.selected,
- };
-
- // 默认属性
- static defaultProps = {
- selectedChanged: false,
- selectedTextColor: '#333333',
- unSelectedTextColor: '#333333',
- // selectedImg: Images.InvoiceInfo.RadioButtonSelected,
- // unSelectedImg: Images.InvoiceInfo.RadioButtonUnSelected
- };
-
- render() {
- let marginStyle = {};
- const {
- selectedImg, unSelectedImg, imgSize, text, selectedTextColor, oritation,
- unSelectedTextColor, textSize, drawablePadding, margin, style,
- } = this.props;
- const {selected} = this.state;
-
- // 这个oritation只是RadioGroup用来告诉RadioButton使用marginLeft还是marginTop,单独使用RadioButton时不需要使用
- if (oritation === 'row') {
- marginStyle = {
- flexDirection: 'row',
- alignItems: 'center',
- marginLeft: margin,
- }
- } else {
- marginStyle = {
- flexDirection: 'row',
- alignItems: 'center',
- marginTop: margin,
- }
- }
- return (
- <TouchableOpacity onPress={() => {
- if (this.selectedChanged) {
- this.selectedChanged(selected, !selected);
- }
- this.setState({
- selected: !selected,
- })
- }}>
- <View
- style={[marginStyle, style]}>
- <Image
- style={{width: imgSize, height: imgSize}}
- source={selected ? selectedImg : unSelectedImg}/>
- <Text style={{
- color: selected ? selectedTextColor : unSelectedTextColor,
- fontSize: textSize, marginLeft: drawablePadding
- }}>{text}
- </Text>
- </View>
- </TouchableOpacity>
- );
- }
-
- /**
- * 设置选中与否的状态:true false
- */
- setSelectedState(selectedState) {
- this.setState({
- selected: selectedState,
- });
- }
- }
学习RN时间还很短,还不到一个星期,写的效果也许不好,但是目前只能达到这个水平,等熟练了之后会载来改进。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。