跳到主要内容

QML 组件

Qt Quick 提供了丰富的内置组件,同时也支持创建自定义组件。本章将介绍常用的 Qt Quick Controls 控件、模型视图组件,以及如何设计和实现高质量的自定义组件。

Qt Quick Controls

Qt Quick Controls 是一套现成的 UI 控件,提供了按钮、文本框、滑块等常见控件。使用这些控件可以快速构建应用程序界面。

导入模块

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Button(按钮)

按钮是最基础的交互控件:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 300
title: "Button 示例"

Column {
anchors.centerIn: parent
spacing: 20

// 基本按钮
Button {
text: "基本按钮"
onClicked: console.log("点击了基本按钮")
}

// 带图标的按钮
Button {
text: "保存"
icon.source: "icons/save.png"
icon.width: 16
icon.height: 16
onClicked: saveFile()
}

// 可勾选按钮
Button {
text: checked ? "已启用" : "已禁用"
checkable: true
onCheckedChanged: console.log("状态:", checked)
}

// 自定义样式
Button {
text: "自定义样式"
background: Rectangle {
color: parent.pressed ? "#2980b9" : "#3498db"
radius: 5
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}

function saveFile() {
console.log("保存文件")
}
}

ToolButton(工具按钮)

工具按钮通常用于工具栏,显示图标或简短文本:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 300

header: ToolBar {
RowLayout {
anchors.fill: parent

ToolButton {
text: "新建"
icon.name: "document-new"
onClicked: console.log("新建文件")
}

ToolButton {
text: "打开"
icon.name: "document-open"
onClicked: console.log("打开文件")
}

ToolSeparator {} // 工具栏分隔符

ToolButton {
text: "撤销"
icon.name: "edit-undo"
enabled: canUndo // 根据状态启用/禁用
}

ToolButton {
text: "重做"
icon.name: "edit-redo"
enabled: canRedo
}

Item { Layout.fillWidth: true } // 弹性空间

ToolButton {
text: "设置"
icon.name: "preferences-system"
onClicked: settingsMenu.open()

Menu {
id: settingsMenu
MenuItem { text: "偏好设置" }
MenuItem { text: "关于" }
}
}
}
}

property bool canUndo: false
property bool canRedo: false
}

DelayButton(延迟按钮)

延迟按钮需要长按一段时间后才触发,常用于重要操作确认:

import QtQuick
import QtQuick.Controls

DelayButton {
text: "删除所有数据"
delay: 2000 // 需要按住 2 秒

onProgressChanged: {
console.log("进度:", progress * 100, "%")
}

onActivated: {
console.log("确认删除")
// 执行删除操作
}

background: Rectangle {
color: "lightgray"
radius: 5

Rectangle {
width: parent.width * parent.parent.progress
height: parent.height
color: "red"
radius: 5
}
}
}

RoundButton(圆形按钮)

import QtQuick
import QtQuick.Controls

Row {
spacing: 20

RoundButton {
text: "+"
font.pixelSize: 24
onClicked: count++
}

Text {
text: count
font.pixelSize: 24
anchors.verticalCenter: parent.verticalCenter
}

RoundButton {
text: "-"
font.pixelSize: 24
onClicked: count = Math.max(0, count - 1)
}

property int count: 0
}

TextField(文本输入框)

单行文本输入控件:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 200

Column {
anchors.centerIn: parent
spacing: 15
width: parent.width - 40

// 基本文本框
TextField {
width: parent.width
placeholderText: "请输入用户名"
onTextChanged: console.log("输入:", text)
}

// 带验证的文本框
TextField {
id: emailField
width: parent.width
placeholderText: "请输入邮箱"
validator: RegularExpressionValidator {
regularExpression: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
}
onAccepted: {
if (acceptableInput) {
console.log("有效邮箱:", text)
}
}
}

// 密码输入
TextField {
width: parent.width
placeholderText: "请输入密码"
echoMode: TextInput.Password
}
}
}

TextArea(多行文本框)

多行文本编辑控件:

ScrollView {
width: 300
height: 200

TextArea {
placeholderText: "请输入内容..."
wrapMode: TextArea.Wrap
selectByMouse: true

onTextChanged: {
characterCount.text = "字符数: " + text.length
}
}
}

Text {
id: characterCount
text: "字符数: 0"
}

ComboBox(下拉框)

下拉选择控件:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 200

Column {
anchors.centerIn: parent
spacing: 20

// 简单下拉框
ComboBox {
model: ["北京", "上海", "广州", "深圳"]
onCurrentIndexChanged: {
console.log("选中:", currentText)
}
}

// 带自定义模型的下拉框
ComboBox {
id: cityCombo
width: 200
textRole: "name" // 显示的属性
valueRole: "code" // 值属性

model: ListModel {
ListElement { name: "北京"; code: "BJ" }
ListElement { name: "上海"; code: "SH" }
ListElement { name: "广州"; code: "GZ" }
ListElement { name: "深圳"; code: "SZ" }
}

onCurrentValueChanged: {
console.log("选中城市代码:", currentValue)
}
}

// 可编辑下拉框
ComboBox {
editable: true
model: ListModel {
id: editableModel
}
onAccepted: {
if (find(editText) === -1) {
editableModel.append({text: editText})
}
}
}
}
}

CheckBox 和 RadioButton

复选框和单选按钮:

import QtQuick
import QtQuick.Controls

Column {
spacing: 15

// 复选框
GroupBox {
title: "兴趣爱好"
Column {
CheckBox {
text: "阅读"
checked: true
onCheckedChanged: console.log(text, checked)
}
CheckBox {
text: "运动"
}
CheckBox {
text: "音乐"
}
}
}

// 单选按钮
GroupBox {
title: "性别"
Column {
RadioButton {
text: "男"
checked: true
}
RadioButton {
text: "女"
}
}
}

// 使用 ButtonGroup 管理单选
ButtonGroup { id: radioGroup }

Row {
RadioButton {
text: "选项1"
ButtonGroup.group: radioGroup
}
RadioButton {
text: "选项2"
ButtonGroup.group: radioGroup
}
RadioButton {
text: "选项3"
ButtonGroup.group: radioGroup
}
}
}

Slider 和 SpinBox

滑块和数字输入:

import QtQuick
import QtQuick.Controls

Column {
spacing: 20
width: 300

// 滑块
Row {
spacing: 10

Slider {
id: slider
from: 0
to: 100
value: 50
onValueChanged: sliderValue.text = Math.round(value)
}

Text {
id: sliderValue
text: "50"
}
}

// 带刻度的滑块
Slider {
from: 0
to: 100
stepSize: 10
value: 30
width: parent.width
}

// 数字输入框
SpinBox {
from: 0
to: 100
value: 50
editable: true
onValueChanged: console.log("值:", value)
}

// 浮点数
SpinBox {
from: 0
to: 100
value: 50
stepSize: 0.1
editable: true

property real realValue: value / 10
textFromValue: (value) => (value / 10).toFixed(1)
valueFromText: (text) => parseFloat(text) * 10
}
}

Dialog(对话框)

对话框用于显示消息或获取用户输入:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 300

Column {
anchors.centerIn: parent
spacing: 20

Button {
text: "显示消息对话框"
onClicked: messageDialog.open()
}

Button {
text: "显示确认对话框"
onClicked: confirmDialog.open()
}
}

// 消息对话框
Dialog {
id: messageDialog
title: "提示"
modal: true
standardButtons: Dialog.Ok

Label {
text: "这是一条消息"
}

onAccepted: console.log("确定")
}

// 确认对话框
Dialog {
id: confirmDialog
title: "确认"
modal: true
anchors.centerIn: parent
standardButtons: Dialog.Yes | Dialog.No

Label {
text: "确定要删除吗?"
}

onAccepted: console.log("确认删除")
onRejected: console.log("取消删除")
}
}

布局管理

Qt Quick Layouts 提供了灵活的布局管理器。

RowLayout 和 ColumnLayout

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

ApplicationWindow {
visible: true
width: 400
height: 300

ColumnLayout {
anchors.fill: parent
spacing: 10

// 标题行
RowLayout {
Layout.fillWidth: true

Label {
text: "用户登录"
font.pixelSize: 20
Layout.alignment: Qt.AlignHCenter
}
}

// 输入区域
GridLayout {
Layout.fillWidth: true
columns: 2
rowSpacing: 10
columnSpacing: 10

Label { text: "用户名:" }
TextField {
Layout.fillWidth: true
placeholderText: "请输入用户名"
}

Label { text: "密码:" }
TextField {
Layout.fillWidth: true
placeholderText: "请输入密码"
echoMode: TextInput.Password
}
}

// 按钮区域
RowLayout {
Layout.alignment: Qt.AlignRight
spacing: 10

Button {
text: "登录"
}
Button {
text: "取消"
}
}

// 弹性空间
Item {
Layout.fillHeight: true
}
}
}

StackLayout

栈布局用于切换显示不同页面:

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

ApplicationWindow {
visible: true
width: 400
height: 300

ColumnLayout {
anchors.fill: parent

Row {
spacing: 5

Button {
text: "页面1"
onClicked: stackLayout.currentIndex = 0
}
Button {
text: "页面2"
onClicked: stackLayout.currentIndex = 1
}
Button {
text: "页面3"
onClicked: stackLayout.currentIndex = 2
}
}

StackLayout {
id: stackLayout
Layout.fillWidth: true
Layout.fillHeight: true

// 页面1
Rectangle {
color: "lightblue"
Label {
anchors.centerIn: parent
text: "页面 1"
}
}

// 页面2
Rectangle {
color: "lightgreen"
Label {
anchors.centerIn: parent
text: "页面 2"
}
}

// 页面3
Rectangle {
color: "lightyellow"
Label {
anchors.centerIn: parent
text: "页面 3"
}
}
}
}
}

模型与视图

QML 提供了强大的模型-视图架构来处理数据展示。

ListView

列表视图是最常用的视图组件:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 500

ListView {
id: listView
anchors.fill: parent
anchors.margins: 10

// 数据模型
model: ListModel {
ListElement { name: "张三"; age: 25; city: "北京" }
ListElement { name: "李四"; age: 30; city: "上海" }
ListElement { name: "王五"; age: 28; city: "广州" }
ListElement { name: "赵六"; age: 22; city: "深圳" }
}

// 列表项委托
delegate: Rectangle {
width: listView.width
height: 60
color: index % 2 === 0 ? "#f5f5f5" : "white"

Row {
anchors.fill: parent
anchors.margins: 10
spacing: 20

Text {
text: name
width: 80
font.bold: true
}
Text {
text: age + "岁"
width: 50
}
Text {
text: city
color: "gray"
}
}

MouseArea {
anchors.fill: parent
onClicked: listView.currentIndex = index
}
}

// 高亮选中项
highlight: Rectangle {
color: "lightblue"
radius: 5
}
highlightFollowsCurrentItem: true

// 滚动条
ScrollBar.vertical: ScrollBar {}

// 点击事件
onCurrentIndexChanged: {
console.log("选中:", model.get(currentIndex).name)
}
}
}

ListView 高级特性

ListView 提供了许多高级特性来增强用户体验:

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 500
height: 600
title: "ListView 高级特性"

ListView {
id: listView
anchors.fill: parent

// 按城市分组
model: ListModel {
ListElement { name: "张三"; city: "北京"; department: "技术部" }
ListElement { name: "李四"; city: "北京"; department: "销售部" }
ListElement { name: "王五"; city: "上海"; department: "技术部" }
ListElement { name: "赵六"; city: "上海"; department: "市场部" }
ListElement { name: "钱七"; city: "广州"; department: "技术部" }
}

// 分组标题
section {
property: "city"
criteria: ViewSection.FullString
delegate: Rectangle {
width: listView.width
height: 30
color: "lightgray"

Text {
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
text: section
font.bold: true
color: "darkgray"
}
}
}

// 页眉
header: Rectangle {
width: listView.width
height: 40
color: "#3498db"

Row {
anchors.fill: parent
anchors.margins: 10

Text { text: "姓名"; width: 80; color: "white"; font.bold: true }
Text { text: "部门"; width: 100; color: "white"; font.bold: true }
Text { text: "城市"; width: 80; color: "white"; font.bold: true }
}
}

// 页脚
footer: Rectangle {
width: listView.width
height: 30
color: "#ecf0f1"

Text {
anchors.centerIn: parent
text: "共 " + listView.count + " 条记录"
color: "gray"
}
}

delegate: ItemDelegate {
width: listView.width
text: name + " - " + department + " - " + city

onClicked: listView.currentIndex = index

// 滑动删除
swipe.left: Label {
text: "删除"
color: "white"
verticalAlignment: Label.AlignVCenter
padding: 20
background: Rectangle { color: "red" }

SwipeDelegate.onClicked: {
listView.model.remove(index)
}
}
}

// 滚动位置指示器
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AsNeeded
}

// 快速滚动指示器
boundsBehavior: Flickable.StopAtBounds
flickableDirection: Flickable.VerticalFlick

// 吸附模式
snapMode: ListView.SnapToItem
}
}

下拉刷新和加载更多

import QtQuick
import QtQuick.Controls

ApplicationWindow {
visible: true
width: 400
height: 600

ListView {
id: listView
anchors.fill: parent

model: ListModel { id: listModel }

delegate: ItemDelegate {
width: listView.width
text: "项目 " + (index + 1)
}

// 下拉刷新检测
property bool isRefreshing: false

onContentYChanged: {
if (contentY < -100 && !isRefreshing) {
isRefreshing = true
refreshIndicator.running = true
// 模拟刷新
refreshTimer.start()
}
}

// 到底部加载更多
onAtYEndChanged: {
if (atYEnd && !isLoading && listModel.count > 0) {
loadMore()
}
}

property bool isLoading: false

// 刷新指示器
BusyIndicator {
id: refreshIndicator
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.topMargin: 20
running: false
}

// 加载更多指示器
footer: BusyIndicator {
width: listView.width
height: 50
running: listView.isLoading
visible: running
}

Timer {
id: refreshTimer
interval: 1500
onTriggered: {
listModel.clear()
for (var i = 0; i < 10; i++) {
listModel.append({ name: "新项目 " + (i + 1) })
}
listView.isRefreshing = false
refreshIndicator.running = false
}
}

function loadMore() {
listView.isLoading = true
loadTimer.start()
}

Timer {
id: loadTimer
interval: 1000
onTriggered: {
for (var i = 0; i < 5; i++) {
listModel.append({ name: "更多项目 " + (listModel.count + i + 1) })
}
listView.isLoading = false
}
}

Component.onCompleted: {
for (var i = 0; i < 20; i++) {
listModel.append({ name: "项目 " + (i + 1) })
}
}
}
}

GridView

网格视图用于展示网格布局的数据:

GridView {
id: gridView
anchors.fill: parent

model: 20 // 简单数字模型

cellWidth: 100
cellHeight: 100

delegate: Rectangle {
width: gridView.cellWidth - 10
height: gridView.cellHeight - 10
color: Qt.hsla((index % 360) / 360, 0.5, 0.5, 1)
radius: 10

Text {
anchors.centerIn: parent
text: index + 1
color: "white"
font.pixelSize: 20
}
}
}

与 C++ 模型交互

// 假设有一个 C++ 模型类 MyModel
ListView {
model: myModel // 从 C++ 设置

delegate: ItemDelegate {
text: model.display // 访问模型数据
width: ListView.view.width

onClicked: {
// 获取模型数据
console.log("选中:", model.display)
}
}
}

自定义组件设计

组件设计原则

设计 QML 组件时应遵循以下原则:

  1. 单一职责:每个组件只做一件事
  2. 属性接口:通过属性暴露可配置项
  3. 信号通信:通过信号与外部通信
  4. 可复用性:避免硬编码,使用属性和别名

组件生命周期

理解 QML 组件的生命周期对于正确管理资源和避免内存泄漏至关重要:

import QtQuick

Item {
id: root

// 组件属性
property string title: ""
property var data: null

// 1. 组件初始化完成
Component.onCompleted: {
console.log("组件创建完成")
initialize()
}

// 2. 组件即将销毁
Component.onDestruction: {
console.log("组件即将销毁")
cleanup()
}

// 3. 属性变化监听
onTitleChanged: {
console.log("标题变化:", title)
}

// 4. 数据变化监听
onDataChanged: {
processData()
}

function initialize() {
// 初始化逻辑
}

function cleanup() {
// 清理资源
data = null
}

function processData() {
// 数据处理
}
}

属性验证

使用属性验证确保组件接收正确的数据:

import QtQuick

Item {
id: root

// 带验证的属性
property int age: 0
property string email: ""
property var items: []

// 年龄验证
onAgeChanged: {
if (age < 0) {
console.warn("年龄不能为负数,已重置为 0")
age = 0
} else if (age > 150) {
console.warn("年龄超出合理范围,已设置为 150")
age = 150
}
}

// 邮箱验证
onEmailChanged: {
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (email !== "" && !emailRegex.test(email)) {
console.warn("邮箱格式不正确:", email)
}
}

// 列表验证
onItemsChanged: {
if (!Array.isArray(items)) {
console.warn("items 必须是数组")
items = []
}
}

// 使用 Binding 进行更严格的验证
Binding {
target: root
property: "age"
value: Math.max(0, Math.min(150, userAge))
when: userAge !== root.age
}

property int userAge: 0
}

默认属性(Default Property)

默认属性允许组件以更简洁的方式接收子项:

// MyContainer.qml
import QtQuick

Item {
id: root
width: 200
height: 200

// 声明默认属性
default property alias content: container.children

Rectangle {
id: container
anchors.fill: parent
color: "lightgray"
}
}

// 使用时可以直接添加子项,无需指定属性名
MyContainer {
Rectangle { color: "red"; width: 50; height: 50 }
Rectangle { color: "blue"; width: 50; height: 50 }
// 这些子项会自动添加到 content 属性
}

组件单例模式

创建全局可访问的单例组件:

// AppSettings.qml (单例)
pragma Singleton
import QtQuick

QtObject {
property string theme: "light"
property string language: "zh_CN"
property int fontSize: 14

function toggleTheme() {
theme = theme === "light" ? "dark" : "light"
}
}

// qmldir 文件配置单例
// singleton AppSettings 1.0 AppSettings.qml

// 在其他组件中使用
import QtQuick

Item {
Component.onCompleted: {
console.log("当前主题:", AppSettings.theme)
AppSettings.toggleTheme()
}
}

组件继承和扩展

使用组件扩展创建变体:

// BaseButton.qml - 基础按钮
import QtQuick
import QtQuick.Controls

Button {
id: root

property color normalColor: "#3498db"
property color hoverColor: "#2980b9"
property color pressedColor: "#1a5276"

background: Rectangle {
color: root.pressed ? pressedColor :
(root.hovered ? hoverColor : normalColor)
radius: 5
}

contentItem: Text {
text: root.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}

// DangerButton.qml - 危险按钮(继承自 BaseButton)
BaseButton {
normalColor: "#e74c3c"
hoverColor: "#c0392b"
pressedColor: "#922b21"
}

// SuccessButton.qml - 成功按钮
BaseButton {
normalColor: "#27ae60"
hoverColor: "#229954"
pressedColor: "#1e8449"
}

自定义按钮组件

// 文件:MyButton.qml
import QtQuick
import QtQuick.Controls

Rectangle {
id: root

// 公开的属性接口
property string buttonText: "按钮"
property color buttonColor: "#3498db"
property color pressedColor: "#2980b9"
property color textColor: "white"
property int buttonWidth: 120
property int buttonHeight: 40

// 信号
signal clicked()
signal pressed()
signal released()

// 尺寸
width: buttonWidth
height: buttonHeight
radius: 5

// 颜色状态
color: mouseArea.pressed ? pressedColor : buttonColor

// 行为动画
Behavior on color {
ColorAnimation { duration: 100 }
}

// 文本
Text {
anchors.centerIn: parent
text: root.buttonText
color: root.textColor
font.pixelSize: 14
}

// 鼠标区域
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true

onClicked: root.clicked()
onPressed: root.pressed()
onReleased: root.released()

// 悬停效果
cursorShape: Qt.PointingHandCursor
}
}

使用自定义组件

import QtQuick
import "components" as Components

Item {
width: 400
height: 200

Row {
anchors.centerIn: parent
spacing: 20

Components.MyButton {
buttonText: "确定"
buttonColor: "#27ae60"
onClicked: console.log("确定")
}

Components.MyButton {
buttonText: "取消"
buttonColor: "#e74c3c"
onClicked: console.log("取消")
}
}
}

复杂组件示例:可展开面板

// 文件:ExpandablePanel.qml
import QtQuick
import QtQuick.Controls

Rectangle {
id: root

// 属性接口
property string title: "面板标题"
property alias content: contentLoader.sourceComponent
property bool expanded: false
property int animationDuration: 200

// 信号
signal expandedChanged(bool expanded)

// 尺寸
width: 300
height: header.height + contentContainer.height
color: "white"
border.color: "#e0e0e0"
border.width: 1
radius: 5

// 头部
Rectangle {
id: header
width: parent.width
height: 40
color: "#f5f5f5"
radius: parent.radius

// 底部圆角处理
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: parent.radius
color: parent.color
}

Row {
anchors.fill: parent
anchors.leftMargin: 10
anchors.rightMargin: 10

Text {
text: root.title
font.bold: true
anchors.verticalCenter: parent.verticalCenter
}

Item { width: 1; height: 1; Layout.fillWidth: true }

Text {
text: root.expanded ? "▼" : "▶"
font.pixelSize: 12
anchors.verticalCenter: parent.verticalCenter
}
}

MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.expanded = !root.expanded
root.expandedChanged(root.expanded)
}
}
}

// 内容区域
Rectangle {
id: contentContainer
anchors.top: header.bottom
width: parent.width
height: root.expanded ? contentLoader.implicitHeight : 0
clip: true

Behavior on height {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.OutQuad
}
}

Loader {
id: contentLoader
width: parent.width
anchors.top: parent.top
}
}
}

状态机

QML 提供了状态机来管理组件的不同状态。

基本状态

import QtQuick

Rectangle {
id: root
width: 200
height: 100

// 默认状态
color: "lightgray"

// 状态定义
states: [
State {
name: "active"
PropertyChanges {
target: root
color: "lightgreen"
}
},
State {
name: "disabled"
PropertyChanges {
target: root
color: "lightgray"
opacity: 0.5
}
}
]

// 过渡动画
transitions: [
Transition {
from: "*"
to: "*"
ColorAnimation { duration: 200 }
NumberAnimation { properties: "opacity"; duration: 200 }
}
]

MouseArea {
anchors.fill: parent
onClicked: {
if (root.state === "")
root.state = "active"
else if (root.state === "active")
root.state = "disabled"
else
root.state = ""
}
}
}

状态切换示例

import QtQuick

Rectangle {
id: button
width: 150
height: 50
radius: 25

states: [
State {
name: "normal"
PropertyChanges { target: button; color: "#3498db" }
PropertyChanges { target: label; text: "点击我" }
},
State {
name: "hovered"
PropertyChanges { target: button; color: "#2980b9" }
},
State {
name: "pressed"
PropertyChanges { target: button; color: "#1a5276" }
PropertyChanges { target: label; text: "按下了" }
}
]

state: "normal"

transitions: Transition {
ColorAnimation { duration: 150 }
}

Text {
id: label
anchors.centerIn: parent
color: "white"
}

MouseArea {
anchors.fill: parent
hoverEnabled: true

onEntered: button.state = "hovered"
onExited: button.state = "normal"
onPressed: button.state = "pressed"
onReleased: button.state = "hovered"
}
}

小结

本章介绍了 QML 组件开发的各个方面:

  1. Qt Quick Controls:Button、TextField、ComboBox、Dialog 等常用控件
  2. 布局管理:RowLayout、ColumnLayout、StackLayout、GridLayout
  3. 模型与视图:ListView、GridView 和委托模式
  4. 自定义组件:组件设计原则、属性接口、信号通信
  5. 状态机:状态定义、属性变化、过渡动画

通过合理使用这些组件和技术,可以构建出功能丰富、用户体验良好的 QML 应用程序。

练习

  1. 使用 GridLayout 创建一个完整的登录表单界面
  2. 实现一个自定义开关按钮(Switch)组件
  3. 使用 ListView 创建一个可选择、可删除的列表
  4. 实现一个带动画效果的下拉菜单组件
  5. 使用状态机实现一个多功能按钮(正常、悬停、按下、禁用状态)

参考资源