Kotlin中自定义RadioGroup实现多个RadioButton自动换行

效果图

1.自定义View

Kotlin 复制代码
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.RadioGroup

class FlowRadioGroup @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : RadioGroup(context, attrs) {

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)

        measureChildren(widthMeasureSpec, heightMeasureSpec)

        var maxWidth = 0
        var totalHeight = 0
        var lineWidth = 0
        var maxLineHeight = 0
        var oldHeight: Int
        var oldWidth: Int
        val count = childCount

        for (i in 0 until count) {
            val child = getChildAt(i)
            val params = child.layoutParams as MarginLayoutParams
            oldHeight = maxLineHeight
            oldWidth = maxWidth

            val deltaX = child.measuredWidth + params.leftMargin + params.rightMargin
            if (lineWidth + deltaX + paddingLeft + paddingRight > widthSize) {
                // 触发换行
                maxWidth = Math.max(lineWidth, oldWidth)
                lineWidth = deltaX
                totalHeight += oldHeight
                maxLineHeight = child.measuredHeight + params.topMargin + params.bottomMargin
            } else {
                // 不换行,累加宽度
                lineWidth += deltaX
                val deltaY = child.measuredHeight + params.topMargin + params.bottomMargin
                maxLineHeight = Math.max(maxLineHeight, deltaY)
            }
            if (i == count - 1) {
                totalHeight += maxLineHeight
                maxWidth = Math.max(lineWidth, oldWidth)
            }
        }

        maxWidth += paddingLeft + paddingRight
        totalHeight += paddingTop + paddingBottom

        setMeasuredDimension(
            if (widthMode == MeasureSpec.EXACTLY) widthSize else maxWidth,
            if (heightMode == MeasureSpec.EXACTLY) heightSize else totalHeight
        )
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val count = childCount
        var preLeft = paddingLeft
        var preTop = paddingTop
        var maxHeight = 0

        for (i in 0 until count) {
            val child = getChildAt(i)
            val params = child.layoutParams as MarginLayoutParams

            if (preLeft + params.leftMargin + child.measuredWidth + params.rightMargin + paddingRight > (r - l)) {
                // 换行重置
                preLeft = paddingLeft
                preTop += maxHeight
                maxHeight = child.measuredHeight + params.topMargin + params.bottomMargin
            } else {
                maxHeight = Math.max(maxHeight, child.measuredHeight + params.topMargin + params.bottomMargin)
            }

            val left = preLeft + params.leftMargin
            val top = preTop + params.topMargin
            val right = left + child.measuredWidth
            val bottom = top + child.measuredHeight

            child.layout(left, top, right, bottom)
            preLeft += params.leftMargin + child.measuredWidth + params.rightMargin
        }
    }
}

2.xml中使用

XML 复制代码
            <FlowRadioGroup
                android:id="@+id/rg_dishevaluation_dialog_pucka"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

3.res下drawable文件中

yuanjiao_bg_gray.xml未选择状态

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:width="339dp" android:height="137dp">
        <shape android:shape="rectangle">
            <solid android:color="#BBBBBB" />
            <corners android:topLeftRadius="11dp" android:topRightRadius="11dp" android:bottomLeftRadius="11dp" android:bottomRightRadius="11dp" />
        </shape>
    </item>
</selector>

yuanjiao_bg_blue.xml选中状态

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:width="339dp" android:height="137dp">
        <shape android:shape="rectangle">
            <solid android:color="#ff4a8dff" />
            <corners android:topLeftRadius="11dp" android:topRightRadius="11dp" android:bottomLeftRadius="11dp" android:bottomRightRadius="11dp" />
        </shape>
    </item>
</selector>

check_radio_button.xml选中状态切换

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/yuanjiao_bg_blue" android:state_checked="true"/>
    <item android:drawable="@drawable/yuanjiao_bg_blue" android:state_pressed="true"/>
    <item android:drawable="@drawable/yuanjiao_bg_gray" android:state_checked="false"/>
    <item android:drawable="@drawable/yuanjiao_bg_gray" android:state_pressed="false"/>
</selector>

4.代码中使用

Kotlin 复制代码
list.forEach { it ->
            val radioButton = RadioButton(context).apply {
                id = View.generateViewId()
                layoutParams = RadioGroup.LayoutParams(
                    RadioGroup.LayoutParams.WRAP_CONTENT,
                    TypedValue.applyDimension(
                        TypedValue.COMPLEX_UNIT_DIP,
                        35f,
                        resources.displayMetrics
                    ).toInt()
                )
                setButtonDrawable(android.R.color.transparent)
                gravity = Gravity.CENTER
                setBackgroundResource(R.drawable.check_radio_button)
                setTextColor(ContextCompat.getColor(context, R.color.white))
                text = it.label//RadioButton中显示的文字
                val margin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
                (layoutParams as ViewGroup.MarginLayoutParams).setMargins(margin, 5, margin, 5)
                val paddingH = TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_DIP,
                    10f,
                    resources.displayMetrics
                ).toInt()
                setPadding(paddingH, 0, paddingH, 0)//设置padding
            }
            radioButton.setOnCheckedChangeListener { _, b ->
                it.check = b//list中存储对应RadioButton当前选择状态
            }
            binding.rgDishevaluationDialogTaste.addView(radioButton)
        }
相关推荐
小徐不徐说2 小时前
序列化与反序列化:把 C++ 对象可靠地保存下来
开发语言·数据库·c++·php
ragnwang2 小时前
Notes: Python pybind11 import C++ module
开发语言·c++·python
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 搜索附近的功能概述和分析
java·开发语言·交友
骑着蜗牛撵大象3273 小时前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
Dxy12393102164 小时前
JS DES 加密详解
开发语言·javascript·arcgis
充值内卷4 小时前
我给桌面加了个鱼缸:C++ 写后端,WebView2 里养一座 Three.js 小岛
开发语言·javascript·c++
云和数据.ChenGuang4 小时前
深入解析 Python 的 dir():从用法到 CPython 源码实现
开发语言·人工智能·python·langchain
nice_lcj5205 小时前
深入理解 Java 反射:从 Class 对象到方法调用(含性能实测 + 迷你 IOC 实战 + 面试考点)
java·开发语言·后端·面试
传奇开心果编程5 小时前
【Jetpack Compose娓娓道来】第29课:状态容器与UI事件建模——把“散装状态”变成“可穷举的契约”
android·kotlin·移动开发·android jetpack·ui框架·开发范式·声明式ui
樱花树下的数学喵6 小时前
手写简易 STL string|C++ 字符串底层实现
开发语言·c++