开始学习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 (
{text}
);
这样就可以自定义图片、文字等属性了,有了这些之后,还需要一个点击事件来改变样式的显示,并且提供给外面一个监听,让外面能够知道改变了,首先声明一个成员变量代表监听事件:
selectedChanged;
然后在构造方法中给这个变量赋值:
constructor(props) {
super(props);
this.selectedChanged = props.selectedChanged;
}
然后给最外层的View添加TouchableOpacity组件,添加点击事件:
return (
{
if (this.selectedChanged) { // 这个是判断外界是否传入了这个监听事件,如果没有传入,则直接调用this.selectedChanged会出错
// this.selectedChanged就是调用了我们刚才生命的成员变量,可以代表一个方法,这里传入一个现在的选中状态的值和上一次状态值
this.selectedChanged(selected, !selected);
}
// 点击之后改变state值,该值一旦改变之后就会重新调用render方法进行绘制
this.setState({
selected: !selected,
})
}}>
);
在外界使用:
{
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,只能有一个被选中
使用示例:
{ // 某一个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 (
{
data.map((radioData, index) => {
return (
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);
}}
/>
);
})
}
);
}
/**
* 某一个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";
/**
*
{
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 (
{
if (this.selectedChanged) {
this.selectedChanged(selected, !selected);
}
this.setState({
selected: !selected,
})
}}>
{text}
);
}
/**
* 设置选中与否的状态:true false
*/
setSelectedState(selectedState) {
this.setState({
selected: selectedState,
});
}
}
学习RN时间还很短,还不到一个星期,写的效果也许不好,但是目前只能达到这个水平,等熟练了之后会载来改进。