前言
其实好久之前就想写这篇文章了,然鹅懒癌发作就一直没写。
Tips:本篇博客的源码有参考群里毒药大佬写的代码并加以修改(药佬不爱写技术博客,那就我来写好啦~)。
缘起(为什么要自定义 RadioGroup?)
官方的 RadioGroup 其是 LinearLayout 的子类,然后需要被单选的控件必须是 RadioButton 或 RadioButton 的子类才有效果(源码里有使用 instanceof 进行判断)。
官方 RadioGroup 控件的缺点:
- 由于是 
LinearLayout的子类,所以只能设置 HORIZONTAL(横向) 或者 VERTICAL。 
(纵向)。
- 子类必须是 
RadioButton,有些样式用RadioButton做起来不是很爽。 
实现思路
实现非常简单粗暴
- 遍历子 View 设置点击事件
 - 调用子 View 点击事件的时候清空所有的选中
 - 设置当前被点击的子 View 为选中状态
 
缘灭
引入依赖
    implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
代码实现
思路有了,于是我们可以顺手写出如下代码
BaseRadioGroup.kt
package cn.cqautotest.sunnybeach.widget
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.Group
/**
 * author : A Lonely Cat
 * github : https://github.com/anjiemo/SunnyBeach
 * time   : 2022/10/05
 * desc   : RadioGroup 基类封装
 */
open class BaseRadioGroup @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : Group(context, attrs) {
    override fun setOnClickListener(l: OnClickListener?) {
        super.setOnClickListener(l)
        getViews()?.forEach { it.setOnClickListener(l) }
    }
    open fun getViews() = (parent as? ViewGroup)?.let { parent -> referencedIds.map { parent.findViewById<View?>(it) } }
    open fun dispatchSelect(isSelected: Boolean) {
        getViews()?.forEach { it?.isSelected = isSelected }
    }
}
Tips:这里有一个小细节,我们直接继承了 androidx.constraintlayout.widget 包下的 Group 控件,这样我们就可以方便的在 xml 布局里指定 “ids” 了(后面会说)。
app/main/res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RadioGroup">
        <!-- 默认选中的View id -->
        <attr name="defaultSelectId" format="reference" />
    </declare-styleable>
    
</resources>
RadioGroup.kt
package cn.cqautotest.sunnybeach.widget
import android.content.Context
import android.util.AttributeSet
import android.view.View
import cn.cqautotest.sunnybeach.R
/**
 * author : A Lonely Cat
 * github : https://github.com/anjiemo/SunnyBeach
 * time   : 2022/10/05
 * desc   : RadioGroup 单选按钮组
 */
class RadioGroup @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : BaseRadioGroup(context, attrs) {
    // 默认选中的 id
    private val defaultSelectId: Int
    // 是否初始化
    private var isInitialization = false
    // 选中的监听
    var onSelected: ((View) -> Unit) = {}
    init {
        val attributes = context.obtainStyledAttributes(attrs, R.styleable.RadioGroup)
        defaultSelectId = attributes.getResourceId(R.styleable.RadioGroup_defaultSelectId, View.NO_ID)
        attributes.recycle()
    }
    override fun applyLayoutFeatures() {
        super.applyLayoutFeatures()
        if (defaultSelectId != View.NO_ID && !isInitialization) {
            selected(defaultSelectId)
        }
        getViews()?.forEach {
            it?.setOnClickListener { _ ->
                dispatchSelect(false)
                it.isSelected = true
                onSelected.invoke(it)
            }
        }
    }
    /**
     * 选中某个 View,参数:id
     */
    fun selected(id: Int) {
        dispatchSelect(false)
        getViews()?.find { it.id == id }?.also { it.isSelected = true }?.also(onSelected)
        isInitialization = true
    }
}
食用方式
xml 布局
    <cn.cqautotest.sunnybeach.widget.RadioGroup
        android:id="@+id/radio_group"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:constraint_referenced_ids="tv_iqiyi,btn_submit,iv_cover,ll_container,fl_container" />
只需要参与单选的控件在与自定义 RadioGroup 同一个 ViewGroup 下即可(因为我们是在当前自定义 RadioGroup 控件的父类下查找的控件)实现单选效果。
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <cn.cqautotest.sunnybeach.widget.RadioGroup
            android:id="@+id/radio_group"
            android:layout_width="0px"
            android:layout_height="0px"
            app:constraint_referenced_ids="tv_squad_leader,tv_vice_monitor,tv_commissary_in_charge_of_studies,tv_commissary_in_charge_of_general_affairs,tv_commissary_in_charge_of_organization,tv_commissary_in_charge_of_publicity,tv_commissary_in_charge_of_sports,iv_login_qq"
            app:defaultSelectId="@id/tv_squad_leader" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_squad_leader"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="班长"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_vice_monitor"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="副班长"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
        <com.hjq.shape.view.ShapeImageView
            android:id="@+id/iv_login_qq"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/login_qq_ic"
            app:shape_solidColor="@color/pink"
            app:shape_solidSelectedColor="@color/follow_btn_text_normal_color" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_commissary_in_charge_of_studies"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="学习委员"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_commissary_in_charge_of_general_affairs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="生活委员"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_commissary_in_charge_of_organization"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="组织委员"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_commissary_in_charge_of_publicity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="宣传委员"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
        <com.hjq.shape.view.ShapeTextView
            android:id="@+id/tv_commissary_in_charge_of_sports"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingHorizontal="16dp"
            android:paddingVertical="10dp"
            android:text="体育委员"
            app:shape_solidColor="@color/white"
            app:shape_solidSelectedColor="#F19C4A"
            app:shape_textSelectedColor="@color/white" />
    </LinearLayout>
kotlin 代码
监听选中
    radioGroup.onSelected = { view ->
			// do something...
    }
设置选中
    radioGroup.selected(R.id.btn_submit)
总结
优点
- 不限制参与单选的控件类型,你可以使用 Button、TextView、ImageView、LinearLayout、FrameLayout...
 - 自定义的 RadioGroup 只需要当成一个普通的 View(Gone 状态) 放在参与单选的控件的共同 ViewGroup 中即可
 
缺点
- 由于继承自  
androidx.constraintlayout.widget包下的Group控件,所以需要引入约束布局的包。 
RadioButton + RadioGroup 实现的单选效果,相信大家都很熟啦~
~~而且由于灵活性比较强,这里就不贴图啦。~~
还是补充一下图片吧~


结语
好啦,就写到这里吧 ,如果你想看更多的项目代码,请查看我在 Github 上的 阳光沙滩APP项目 ,你也可以在这里点击下载我写的 阳光沙滩APP客户端 进行体验。
如果对你有帮助的话,欢迎一键三连+关注哦~
          本文由
          A lonely cat
          原创发布于
          阳光沙滩
          ,未经作者授权,禁止转载
        
 
