QML 选择控件:Switch、CheckBox 与 RadioButton

这篇文章围绕 QML 里最常见的三种选择控件展开:Switch、CheckBox 和 RadioButton。它们几乎覆盖了日常开发里所有"勾选 / 取消"类的交互场景。

  • Switch --- 二态切换,适合设置项启用/禁用
  • CheckBox --- 多选框,支持父子联动
  • RadioButton --- 单选按钮,同一容器内自动互斥

Switch - 开关按钮

两个设置项的开关,分别控制 Wi-Fi 和 Bluetooth 状态。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    property bool wifiEnabled: true
    property bool bluetoothEnabled: false

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        GridLayout {
            Layout.alignment: Qt.AlignHCenter
            columns: 2
            rowSpacing: 12
            columnSpacing: 12

            Text { text: qsTr("Wi-Fi:") }
            Switch {
                checked: wifiEnabled
                onClicked: wifiEnabled = !wifiEnabled
            }
            Text { text: qsTr("Bluetooth:") }
            Switch {
                checked: bluetoothEnabled
                onClicked: bluetoothEnabled = !bluetoothEnabled
            }
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

Switch 本身带有 checked 属性,但示例里没有直接让它自己管自己,而是把状态提到了外部:

  • checked: wifiEnabled 只做显示绑定,Switch 的开/关状态永远跟随 wifiEnabled
  • onClicked: wifiEnabled = !wifiEnabled 在点击时把外部状态翻一次,UI 随后自动刷新。

这样做的好处是状态与 UI 解耦。后续如果你想换成网络请求返回值、配置文件读取,或者加一组音效/震动反馈,都只需要改 wifiEnabled 的赋值位置,而不用动 Switch 本身的绑定。相比直接监听 onCheckedChanged,显式在 onClicked 里切换更符合"用户主动触发"的语义,也不容易在程序初始化时被误触发。

适用场景:设置页、功能开关、偏好配置。

CheckBox - 复选框

一个父节点和两个孩子节点,父子选中状态自动联动。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        ButtonGroup {
            id: childGroup
            exclusive: false
            checkState: parentBox.checkState
            buttons: childColumn.children
        }

        CheckBox {
            id: parentBox
            text: "父节点"
            checkState: childGroup.checkState
            Layout.alignment: Qt.AlignLeft
        }

        Column {
            id: childColumn
            Layout.leftMargin: 15
            Layout.alignment: Qt.AlignLeft
            spacing: 15

            CheckBox { text: "子节点 1" }
            CheckBox { text: "子节点 2" }
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

父子联动的核心是两组 checkState 的互相引用,Qt 会帮你处理中间状态:

  • ButtonGroup 把两个子 CheckBox 归为一组。exclusive: false 表示它不是单选组,子选项可以多选。
  • buttons: childColumn.children 是批量绑定写法,只要放在 childColumn 里的 CheckBox 都会自动加入分组,省得一个个写 ButtonGroup.group
  • parentBox.checkState: childGroup.checkState 让父框跟随子框:全选为勾选,全不选为未勾选,部分选中就是半选状态。
  • 反过来 childGroup.checkState: parentBox.checkState 让点击父框时,子框会一次性全部选中或取消。

所以这两行看起来是双向绑定,其实是 Qt 的 checkState 在内部做了收敛:子框状态决定父框显示,父框点击又会同步所有子框。写权限树、分类筛选时直接套这个结构,基本不用再手写递归。

适用场景:权限选择、分类筛选、树形多选。

RadioButton - 单选按钮

两组单选按钮,展示"同一容器内互斥"和"跨容器不互斥"的区别。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        RadioButton {
            text: "在布局外-单选按钮"
            Layout.alignment: Qt.AlignLeft
            onClicked: {
                textResult.text = "在同一布局内的按钮才能进行单选";
            }
        }

        Column {
            id: childColumn
            Layout.leftMargin: 15
            Layout.alignment: Qt.AlignLeft
            spacing: 15

            RadioButton {
                text: "在布局内-单选按钮1"
                onClicked: {
                    textResult.text = "点击了" + text;
                }
            }
            RadioButton {
                text: "在布局内-单选按钮2"
                onClicked: {
                    textResult.text = "点击了" + text;
                }
            }
        }

        Text {
            id: textResult
            Layout.alignment: Qt.AlignLeft
        }

        Item { Layout.fillHeight: true }
    }
}

关键逻辑解析

RadioButton 的互斥逻辑比想象中省事,但也有容易踩坑的地方:

  • 同一个父容器里的 RadioButton 会自动互斥,这是默认行为,不需要额外写 ButtonGroup。示例中"布局内-单选按钮 1"和"布局内-单选按钮 2"都在 childColumn 里,所以点一个会自动取消另一个。
  • "布局外-单选按钮"放在 ColumnLayout 根层级,和 childColumn 不是同一个直接父容器,因此它和里面的两个 RadioButton 互不干扰。
  • 实际布局中经常遇到单选按钮分散在不同 Row、Column 或自定义组件里的情况。这时候需要显式建一个 ButtonGroup,把 buttons 指向这些跨容器的 RadioButton,才能强制它们互斥。

另外,RadioButton 的 autoExclusive 默认是 true。如果你某个特殊场景下不想互斥,把它设成 false 即可,不过这种情况比较少见。

适用场景:性别选择、支付方式、单选配置项。

对比表格

控件 状态数 是否互斥 关键机制 典型场景
Switch 2 checked + 取反 设置开关
CheckBox 3 ButtonGroup + checkState 多选、父子联动
RadioButton 2 同一容器内自动互斥 容器隐式分组 单选

运行验证

  1. Qt Creator 打开 qml_button/CMakeLists.txt
  2. Ctrl+R 运行;
  3. 左侧导航切换到对应 demo,测试开关切换、复选框联动、单选互斥。

扩展复用方向

  • 把 Switch 封装成设置项组件,统一标签、开关和分隔线。
  • 用 CheckBox 做表格行选择,结合 ButtonGroup 实现全选/反选。
  • ButtonGroup 显式管理跨布局的 RadioButton,实现复杂表单分组。

已验证环境

相关推荐
Quz1 天前
QML 基础按钮:Button、RoundButton 与 DelayButton
qt
Quz1 天前
QML ToolTip 组件:图标、多行文本与阴影
qt
xcyxiner1 天前
DicomViewer14(读取图像按固定窗宽窗位显示)
qt
马里马里奥-2 天前
从零搭建AI Agent工具链的技术文章大纲
开发语言·qt
Quz2 天前
QML 与 JavaScript 交互方式:内联函数、外部文件、信号槽与工作线程
javascript·qt
Quz2 天前
QML ToolTip 组件:延迟提示与自定义外观
qt
wWYy.2 天前
RPC详解
qt·rpc·php
Fu_Lin_2 天前
Qt嵌入式从零基础到精通(01):Qt嵌入式开发全景图:从桌面Qt到ARM Linux
linux·arm开发·qt·qt5·嵌入式linux
海清河晏1112 天前
Qt实战:从零构建美化登录界面
开发语言·c++·qt