前言
其实好久之前就想写这篇文章了,然鹅懒癌发作就一直没写。
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
原创发布于
阳光沙滩
,未经作者授权,禁止转载