在 RN 中,官方并没有提供单选组件,如果应用开发中涉及到单选功能,就需要开发者使用第三方开源库或者自己封装单选组件。
通常,一个正常的单选功能会包含若干个子选项,每个子选项的前面有一个标识勾选状态的圆环,当某个选项被选中时圆环会变成实心,表示选中状态。如下图所示:
要完成自定义单选功能,首先需要自定义一个单选按钮组件。通过分析可以发现,单选按钮的左边是图片,右边是描述文字,按钮的图片有选中和未选中两种状态。基于此,在开发的时候可以定义一个状态变量 selected 来记录按钮的选中状态,为 true 时表示选中,为 false 时表示未选中。
state = { selected: this.props.selected, // 状态由外部传入}为了保证自定义组件的通用性,除了状态是由外部传入的,JSX 中要渲染的图片和文字以及按钮的样式也需要由外部传入,因此可以定义如下一些必要的参数或属性供外部传入。
const { text, drawablePadding, style } = this.props当单选按钮接收到外部传入的属性后,就可以执行渲染操作了。同时,当改变按钮的选中状态之后,还需要将状态传递出去,此时就需要使用默认属性。自定义单选按钮的示例如下:
import React, { PureComponent } from 'react'import { View, Pressable, Text, Image, StyleSheet } from 'react-native'let selectedImage = require('../assets/radio_selted.png')let unSelectedImage = require('../assets/radio_select.png')
export default class RadioButton extends PureComponent { static defaultProps = { selectedChanged: false, selectedTextColor: '#F83D2B', unSelectedTextColor: '#333333', }
state = { selected: this.props.selected, }
constructor(props) { super(props) this.selectedChanged = props.selectedChanged }
// 每个按钮的点击事件 pressHandle() { this.selectedChanged(this.state.selected) this.setState({ selected: !this.state.selected, }) }
render() { const { text, drawablePadding } = this.props const { selected } = this.state
return ( <Pressable onPress={this.pressHandle.bind(this)}> <View style={styles.radioStyle}> {/* 左边图片 */} <Image style={styles.image} source={selected ? selectedImage : unSelectedImage} /> {/* 右边文字 */} <Text style={{ color: selected ? this.props.selectedTextColor : this.props.unSelectedTextColor, marginLeft: drawablePadding, fontSize: 18, }} > {text} </Text> </View> </Pressable> ) }
setSelectedState(state) { this.setState({ selected: state, }) }}
const styles = StyleSheet.create({ radioStyle: { flexDirection: 'row', alignItems: 'center', marginLeft: 20, marginRight: 20, marginTop: 10, marginBottom: 10, }, image: { width: 22, height: 22, }, text: { flexDirection: 'row', alignItems: 'center', },})上面的代码完成了一个自定义单选按钮的功能,并不能真正的实现单选功能。因为一个正常的单选功能会包含若干子选项,而且只能有一个选项被选中。
因此,要完成自定义单选功能,还需要定义一个容器组件,这个容器组件会包含多个单选按钮,并且只有一个能选中。通过分析,自定义的单选组件的数据源、排列方向和默认选中项都需要由外界传入,因此自定义的单选组件至少需要提供如下一些属性供使用方传入。
const { data, orientation, defaultValue, drawablePadding } = this.props当然,除了上面的一些必要属性,开发者还可以根据实际需要自由定制。下面是自定义单选按钮的容器组件示例代码:
import React, { PureComponent } from 'react'import { View } from 'react-native'import RadioButton from './RadioButton'
export default class RadioGroup extends PureComponent { constructor(props) { super(props) this.state = { currentIndex: -1, // 当前选中的索引 dataArray: [], itemChange: props.itemChange, } }
render() { // 获取参数 const { data, orientation, defaultValue, drawablePadding } = this.props
return ( <View style={{ flexDirection: orientation }}> {data.map((radioData, index) => { return ( <RadioButton index={index} selected={index === defaultValue ? true : false} key={index} ref={(radioButton) => this.state.dataArray.push(radioButton)} text={radioData.text} oritation={orientation} drawablePadding={drawablePadding} selectedChanged={() => { this.change(index) }} /> ) })} </View> ) }
change(index) { this.state.currentIndex = index console.log(this.state.dataArray) this.state.dataArray.map((refer, index2) => { if (refer !== null) { refer.setSelectedState(index2 === this.state.currentIndex) } }) this.state.itemChange(this.state.currentIndex) }}在上面的自定义单选按钮容器组件中,当某个子选项被选中后,会调用 change 方法改变选中按钮的状态,进而通知组件进行视图更新。
至此,自定义单选组件就算基本开发完成了。使用时,只需要根据要求传入必要的属性即可。
<RadioGroup orientation="row" data={data} defaultValue={0} drawablePadding={8} itemChange={(index) => { alert(index) }}></RadioGroup>下面是我们在根组件中进行测试的完整代码:
import React, { PureComponent } from 'react'import { View, Text, StyleSheet } from 'react-native'import RadioGroup from './components/RadioGroup'
export default class App extends PureComponent { constructor(props) { super(props) this.state = { data: [{ text: '个人' }, { text: '单位' }, { text: '其他' }], } } render() { return ( <View style={styles.container}> <Text style={styles.title}>发票抬头</Text> <RadioGroup orientation="column" data={this.state.data} defaultValue={0} //默认选中的值 drawablePadding={10} //图片与文字的间距 itemChange={(index) => { alert(index) }} ></RadioGroup> </View> ) }}
const styles = StyleSheet.create({ container: { marginTop: 35, }, title: { height: 30, fontSize: 20, borderColor: 'black', marginLeft: 15, fontWeight: 'bold', },})