QML自定义组件
自定义组件是 QML 开发中的核心概念,它允许您创建可重用的 UI 元素和逻辑单元。以下是创建和使用自定义组件的完整方法。
1. 基本自定义组件创建
创建单独组件文件 (推荐方式)
qml
// MyButton.qml(单独一个qml文件)
import QtQuick 2.15
Rectangle {
id: root
// 可自定义属性
property string text: "Button"
property color textColor: "white"
signal clicked
width: 120; height: 40
color: mouseArea.containsMouse ? "#3498db" : "#2980b9"
radius: 5
Text {
anchors.centerIn: parent
text: root.text
color: root.textColor
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked()
}
}
内联组件 (适用于简单组件)
qml
Item {
component InlineButton: Rectangle {
property string text
width: 100; height: 30
color: "lightgray"
Text { anchors.centerIn: parent; text: parent.text }
}
InlineButton { text: "OK" }
InlineButton { text: "Cancel" }
}
2. 组件属性与接口
定义属性
qml
// SmartInput.qml
Item {
id: root
// 基本属性
property string placeholder: ""
property string text: ""
// 带默认值的属性
property int maxLength: 100
property bool isValid: text.length <= maxLength
// 只读属性
readonly property bool hasText: text.length > 0
// 别名属性
property alias cursorPosition: input.cursorPosition
// 信号
signal accepted(string text)
signal validationChanged(bool valid)
// 子元素
TextInput {
id: input
anchors.fill: parent
text: root.text
onTextChanged: {
root.text = text
root.validationChanged(root.isValid)
}
onAccepted: root.accepted(text)
}
}
使用属性
qml
SmartInput {
id: myInput
width: 200; height: 30
placeholder: "请输入用户名"
maxLength: 20
onAccepted: console.log("输入内容:", text)
onValidationChanged: (valid) => {
console.log("验证状态:", valid)
}
}
3. 组件间通信
方法1: 信号与槽
qml
// ToggleSwitch.qml
Item {
id: root
signal toggled(bool on)
property bool on: false
MouseArea {
anchors.fill: parent
onClicked: {
root.on = !root.on
root.toggled(root.on)
}
}
}
// 使用
ToggleSwitch {
onToggled: (isOn) => {
console.log("开关状态:", isOn)
}
}
方法2: 函数调用
qml
// Counter.qml
Item {
id: root
property int count: 0
function increment() {
count++
}
function decrement() {
count--
}
}
// 使用
Counter {
id: myCounter
}
Button {
text: "增加"
onClicked: myCounter.increment()
}
4. 动态组件
动态创建
qml
Item {
id: container
width: 300; height: 300
function createDynamicItem() {
var component = Qt.createComponent("DynamicItem.qml")
if (component.status === Component.Ready) {
var obj = component.createObject(container, {
"x": Math.random() * 250,
"y": Math.random() * 250
})
}
}
}
动态加载 (Loader)
qml
Item {
width: 300; height: 300
Loader {
id: componentLoader
anchors.fill: parent
source: "DynamicComponent.qml"
}
Button {
text: "重新加载"
onClicked: {
componentLoader.source = ""
componentLoader.source = "DifferentComponent.qml"
}
}
}
5. 高级组件模式
带模型的组件
qml
// ListItem.qml
Rectangle {
id: root
width: ListView.view ? ListView.view.width : 100
height: 50
required property string name
required property string description
color: mouseArea.containsMouse ? "#f0f0f0" : "white"
Column {
anchors.fill: parent
anchors.margins: 5
Text { text: name; font.bold: true }
Text { text: description; font.pixelSize: 10 }
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
}
}
// 使用
ListView {
width: 200; height: 300
model: ListModel {
ListElement { name: "项目1"; description: "描述1" }
ListElement { name: "项目2"; description: "描述2" }
}
delegate: ListItem {}
}
主题化组件
qml
// ThemedButton.qml
Rectangle {
id: root
// 主题属性
property color primaryColor: "#3498db"
property color hoverColor: Qt.darker(primaryColor, 1.2)
property color textColor: "white"
property string text: "Button"
signal clicked
width: 120; height: 40
radius: 5
color: mouseArea.containsMouse ? hoverColor : primaryColor
Text {
anchors.centerIn: parent
text: root.text
color: root.textColor
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked()
}
}
6. 最佳实践
-
单一职责原则:每个组件应该只负责一个功能
-
明确接口:通过属性和信号定义清晰的组件接口
-
合理命名:使用描述性名称 (如
PrimaryButton
而非MyButton
) -
文档注释:为组件添加注释说明用法
-
默认值合理:为属性设置合理的默认值
-
性能优化:避免在组件内部进行复杂计算
7. 实际案例:带图标的按钮
IconButton.qml
Item {
id: root
// 公共API
property alias icon: iconImage.source
property alias text: label.text
property color color: "#3498db"
property color hoverColor: Qt.darker(color, 1.2)
signal clicked
width: Math.max(iconImage.width + label.width + 20, 80)
height: 40
Rectangle {
anchors.fill: parent
radius: 4
color: mouseArea.containsMouse ? root.hoverColor : root.color
Row {
anchors.centerIn: parent
spacing: 8
Image {
id: iconImage
sourceSize.width: 20
sourceSize.height: 20
}
Text {
id: label
color: "white"
font.pixelSize: 14
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: root.clicked()
}
// 组件行为
Behavior on color {
ColorAnimation { duration: 200 }
}
}
另外qml文件中引用
// 使用示例
IconButton {
icon: "qrc:/icons/save.png"
text: "保存"
onClicked: console.log("保存按钮点击")
}