当前位置:   article > 正文

自定义React Native的RadioButton和RadioGroup(单选按钮)_react native 单选框

react native 单选框

开始学习React Native也有5天了,在项目时发现RN没有给提供RadioButton和RadioGroup这两个组件,只有CheckBox组件,但是项目中确实有好处需要使用到RadioButton和RadioGroup,既然没有,那就自己写吧。

效果图:


1.RadioButton

需求分析:

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为未选中状态:

  1. state = {
  2. // 让当前的变量为传递过来的是否选中的状态,在使用RadioButton时是可以指定属性selected,在这里接收
  3.  selected: this.props.selected,
  4. };

state变量和动态控制一个组件的改变,但是组件改变之前仍然可能会显示一些东西,这些东西用props来控制,而且可以用这些props定义一些默认的属性,例如我们可以定义默认的文字颜色等:

  1. // 默认属性
  2. static defaultProps = {
  3. selectedChanged: false,
  4. selectedTextColor: '#333333',
  5. unSelectedTextColor: '#333333',
  6. };

下面就是在render方法中渲染图片和文字,以及控制一些style属性,首先获取到传递过来的各种属性值以及当前的状态值:

  1. const {
  2. selectedImg, unSelectedImg, imgSize, text, selectedTextColor, oritation,
  3. unSelectedTextColor, textSize, drawablePadding, margin, style,
  4. } = this.props;
  5. const {selected} = this.state;

因为在渲染时多个不同的RadioButton排列方向可以是横纵方向,所以这个时候就需要注意传递过来的margin是marginLeft还是marginTop,所以先定义一个变量style,根据方向的不同,来决定到底是marginLeft还是marginTop:

  1. let marginStyle = {};
  2. if (oritation === 'row') {
  3. marginStyle = {
  4.     flexDirection: 'row',
  5.     alignItems: 'center',
  6.     marginLeft: margin,
  7. }
  8. } else {
  9. marginStyle = {
  10. flexDirection: 'row',
  11. alignItems: 'center',
  12. marginTop: margin,
  13. }
  14. }

然后返回一个整体的View即可:这里需要注意,在使用时我们可能会给这个RadioButton添加style属性,例如marginLeft,marginTop等,这个是在外面设置的,在内部我们同样会设置style属性,这个style是用来控制图片和文字这2个元素为横向排列,所以需要把外面传递过来的属性和内部的属性合到一起,多个属性使用数组就行,例如下面的:

style={[marginStyle, style]},然后就是根据state以及传入过来的属性设置具体的值。

  1. return (
  2. <View style={[marginStyle, style]}>
  3. <Image
  4. style={{width: imgSize, height: imgSize}}
  5. source={selected ? selectedImg : unSelectedImg}/>
  6. <Text style={{
  7. color: selected ? selectedTextColor : unSelectedTextColor,
  8. fontSize: textSize, marginLeft: drawablePadding
  9.         }}>{text}
  10. </Text>
  11. </View>
  12. );

这样就可以自定义图片、文字等属性了,有了这些之后,还需要一个点击事件来改变样式的显示,并且提供给外面一个监听,让外面能够知道改变了,首先声明一个成员变量代表监听事件:

selectedChanged;

然后在构造方法中给这个变量赋值:

  1. constructor(props) {
  2. super(props);
  3. this.selectedChanged = props.selectedChanged;
  4. }

然后给最外层的View添加TouchableOpacity组件,添加点击事件:

  1. return (
  2. <TouchableOpacity onPress={() => {
  3. if (this.selectedChanged) { // 这个是判断外界是否传入了这个监听事件,如果没有传入,则直接调用this.selectedChanged会出错
  4. // this.selectedChanged就是调用了我们刚才生命的成员变量,可以代表一个方法,这里传入一个现在的选中状态的值和上一次状态值                   
  5.                     this.selectedChanged(selected, !selected);
  6. }
  7. // 点击之后改变state值,该值一旦改变之后就会重新调用render方法进行绘制
  8.                 this.setState({
  9. selected: !selected,
  10. })
  11. }}>
  12. <View
  13. ...
  14. </View>
  15. </TouchableOpacity>
  16. );

在外界使用:

  1. <RadioButton
  2. selected={true}
  3. selectedImg={Images.InvoiceInfo.RadioButtonSelected}
  4. unSelectedImg={Images.InvoiceInfo.RadioButtonUnSelected}
  5. imgSize={20}
  6. text='个人'
  7. textSize={12}
  8. drawablePadding={8}
  9. selectedChanged={(oldState, newState) => {
  10. console.log(oldState + "--" + newState);
  11. }}/>

2.RadioGroup

使用RadioButton大部分情况是多个共同使用,而且只能有一个被选中,android中就有这样的组件,但是在RN中没有找到,其实这个也很容易实现,原理是通过RadioGroup来生成多个RadioButton并且持有这些RadioButton的引用,当一个被选中的时候把其他的置为不选中,使用RadioGroup时给这个RadioGroup传递一个数组即可,然后RadioGroup通过数组来创建RadioGroup,因为同样要指定RadioButton的样式,所以在外部使用时直接把style的各种样式和属性一并传递给RadioGroup,RadioGroup在创建RadioButton时把这些样式属性再传递给RadioButton即可,原理很简单,直接上代码:


  1. import React, {Component} from 'react';
  2. import {
  3. View,
  4. } from 'react-native';
  5. import RadioButton from "./RadioButton";
  6. /**
  7. * RadioGroup:
  8. * 里面只能存放RadioButton,生成多少个RadioButton,需要传递data数组,不管生成多少个RadioButton,只能有一个被选中
  9. 使用示例:
  10. <RadioGroup
  11. style={{marginLeft: 20, marginTop: 23}} // 指定RadioGroup的样式
  12. oritation='column' // 里面RadioButton是横向排列还是纵向排列:'column','row'
  13. margin={15} // 如果oritation='column'则代表多个RadioButton横向间距'row',则代表多个RadioButton纵向间距
  14. data={[{"text": "个人"}, {"text": "单位"}, {"text": "其他"}]} // data需要传递一个数组
  15. selectedImg={Images.InvoiceInfo.RadioButtonSelected} // 被选状态时的图片
  16. unSelectedImg={Images.InvoiceInfo.RadioButtonUnSelected} // 未被选状态时的图片
  17. imgSize={20} // 图片的大小宽高一样
  18. text='单位' // 文字
  19. textSize={12} // 文字大小
  20. drawablePadding={8} // 文字和图片之间的距离
  21. itemChange={(index) => { // 某一个item被选中时的事件监听,会返回当前选中的item的索引位置
  22. ToastAndroid.show("" + index, ToastAndroid.SHORT);
  23. }}
  24. />
  25. */
  26. export default class RadioGroup extends Component {
  27. // 当前选中的item索引
  28. currentIndex = -1;
  29. // 存放所有的RadioButton的引用
  30. referencesArray = [];
  31. // 选中某一个item的回调监听
  32. itemChange;
  33. constructor(props) {
  34. super(props);
  35. this.itemChange = props.itemChange;
  36. }
  37. render() {
  38. // 清空数组,避免多次render重复向数组中添加
  39. this.referencesArray = [];
  40. const {
  41. data, selectedImg, unSelectedImg, imgSize, selectedTextColor, oritation,
  42. unSelectedTextColor, textSize, drawablePadding, margin, style,
  43. } = this.props;
  44. return (
  45. <View style={[style, {flexDirection: oritation}]}>
  46. {
  47. data.map((radioData, index) => {
  48. return (
  49. <RadioButton
  50. key={index}
  51. ref={radioButton => this.referencesArray.push(radioButton)}
  52. text={radioData.text}
  53. selectedImg={selectedImg}
  54. unSelectedImg={unSelectedImg}
  55. imgSize={imgSize}
  56. selectedTextColor={selectedTextColor}
  57. unSelectedTextColor={unSelectedTextColor}
  58. textSize={textSize}
  59. oritation={oritation}
  60. drawablePadding={drawablePadding}
  61. margin={index === 0 ? null : margin}
  62. selectedChanged={() => {
  63. this.change(index);
  64. }}
  65. />
  66. );
  67. })
  68. }
  69. </View>
  70. );
  71. }
  72. /**
  73. * 某一个item选中后的事件
  74. * @param index 当前选中的item的索引
  75. */
  76. change(index) {
  77. this.currentIndex = index;
  78. // 遍历引用数组,通知每一个RadioButton改变状态
  79. this.referencesArray.map((refer, index2) => {
  80. if (refer !== null) {
  81. refer.setSelectedState(index2 === this.currentIndex);
  82. }
  83. });
  84. // 调用回调监听
  85. this.itemChange(this.currentIndex);
  86. }
  87. }

把RadioButton的全部代码一起附上:

  1. import React, {Component} from 'react';
  2. import {
  3. Text,
  4. View,
  5. TouchableOpacity,
  6. Image,
  7. } from 'react-native';
  8. import Images from "../Images";
  9. /**
  10. *
  11. <RadioButton
  12. selected={true}
  13. selectedImg={Images.InvoiceInfo.RadioButtonSelected}
  14. unSelectedImg={Images.InvoiceInfo.RadioButtonUnSelected}
  15. imgSize={20}
  16. text='个人'
  17. textSize={12}
  18. drawablePadding={8}
  19. selectedChanged={(oldState, newState) => {
  20. console.log(oldState + "--" + newState);
  21. }}/>
  22. */
  23. export default class RadioButton extends Component {
  24. selectedChanged;
  25. constructor(props) {
  26. super(props);
  27. this.selectedChanged = props.selectedChanged;
  28. }
  29. state = {
  30. selected: this.props.selected,
  31. };
  32. // 默认属性
  33. static defaultProps = {
  34. selectedChanged: false,
  35. selectedTextColor: '#333333',
  36. unSelectedTextColor: '#333333',
  37. // selectedImg: Images.InvoiceInfo.RadioButtonSelected,
  38. // unSelectedImg: Images.InvoiceInfo.RadioButtonUnSelected
  39. };
  40. render() {
  41. let marginStyle = {};
  42. const {
  43. selectedImg, unSelectedImg, imgSize, text, selectedTextColor, oritation,
  44. unSelectedTextColor, textSize, drawablePadding, margin, style,
  45. } = this.props;
  46. const {selected} = this.state;
  47. // 这个oritation只是RadioGroup用来告诉RadioButton使用marginLeft还是marginTop,单独使用RadioButton时不需要使用
  48. if (oritation === 'row') {
  49. marginStyle = {
  50. flexDirection: 'row',
  51. alignItems: 'center',
  52. marginLeft: margin,
  53. }
  54. } else {
  55. marginStyle = {
  56. flexDirection: 'row',
  57. alignItems: 'center',
  58. marginTop: margin,
  59. }
  60. }
  61. return (
  62. <TouchableOpacity onPress={() => {
  63. if (this.selectedChanged) {
  64. this.selectedChanged(selected, !selected);
  65. }
  66. this.setState({
  67. selected: !selected,
  68. })
  69. }}>
  70. <View
  71. style={[marginStyle, style]}>
  72. <Image
  73. style={{width: imgSize, height: imgSize}}
  74. source={selected ? selectedImg : unSelectedImg}/>
  75. <Text style={{
  76. color: selected ? selectedTextColor : unSelectedTextColor,
  77. fontSize: textSize, marginLeft: drawablePadding
  78. }}>{text}
  79. </Text>
  80. </View>
  81. </TouchableOpacity>
  82. );
  83. }
  84. /**
  85. * 设置选中与否的状态:true false
  86. */
  87. setSelectedState(selectedState) {
  88. this.setState({
  89. selected: selectedState,
  90. });
  91. }
  92. }
学习RN时间还很短,还不到一个星期,写的效果也许不好,但是目前只能达到这个水平,等熟练了之后会载来改进。



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/865859
推荐阅读
相关标签
  

闽ICP备14008679号