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 组件时应遵循以下原则:
- 单一职责:每个组件只做一件事
- 属性接口:通过属性暴露可配置项
- 信号通信:通过信号与外部通信
- 可复用性:避免硬编码,使用属性和别名
组件生命周期
理解 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 组件开发的各个方面:
- Qt Quick Controls:Button、TextField、ComboBox、Dialog 等常用控件
- 布局管理:RowLayout、ColumnLayout、StackLayout、GridLayout
- 模型与视图:ListView、GridView 和委托模式
- 自定义组件:组件设计原则、属性接口、信号通信
- 状态机:状态定义、属性变化、过渡动画
通过合理使用这些组件和技术,可以构建出功能丰富、用户体验良好的 QML 应用程序。
练习
- 使用 GridLayout 创建一个完整的登录表单界面
- 实现一个自定义开关按钮(Switch)组件
- 使用 ListView 创建一个可选择、可删除的列表
- 实现一个带动画效果的下拉菜单组件
- 使用状态机实现一个多功能按钮(正常、悬停、按下、禁用状态)