前言
在使用 Qt Quick 開發的過程中,有時我們會遇到這樣的需求,界面視窗顯示出來後,某些 Item 并不存在,當發生了滑鼠點選或者某種事件觸發之後,才會去建立這些Item并顯示出來,這就是我們經常說的動态建立元件。本章節部落客就整理了幾種在 QML 中動态建立元件的方法。
方法一:Model + View
首先需要定義好 view 和 model,當我們擷取到一組新的資料需要在界面上顯示的時候,可以通過在model中添加資料的方式,在view中動态顯示。
示例:
import QtQuick 2.12
import QtQuick.Window 2.12
Window {
visible: true
width: 640
height: 480
ListView {
id: view
width: parent.width
height: 300
clip: true
focus: true
model: ListModel {
id: model
ListElement { name: "Apple"; cost: 2.45 }
}
delegate: Component {
Item {
width: 180; height: 40
Column {
anchors.verticalCenter: parent.verticalCenter
Text { text: '<b>Name:</b> ' + name }
Text { text: '<b>Number:</b> ' + cost }
}
}
}
highlight: Rectangle {
color: "lightsteelblue"
radius: 5
}
add: Transition {
NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
}
}
Rectangle {
width: 120
height: 80
anchors.bottom: parent.bottom
anchors.bottomMargin: 50
anchors.horizontalCenter: parent.horizontalCenter
color: "yellow"
Text {
anchors.centerIn: parent
font.pixelSize: 28
font.family: "微軟雅黑"
color: "black"
text: qsTr("add")
}
MouseArea {
anchors.fill: parent
onClicked: {
addItemToListView("banana", 5)
addItemToListView("orange", 3.6)
addItemToListView("pear", 1.8)
addItemToListView("watermelon", 8.5)
}
}
}
function addItemToListView(name, cost)
{
model.append({ "name":name, "cost":cost })
}
}
方法二:Loader +Component
QT開發交流+赀料君羊:714620761
MyItem1.qml
import QtQuick 2.0
Rectangle {
width: 640
height: 300
color: "yellow"
Text {
anchors.centerIn: parent
font.pixelSize: 48
font.family: "微軟雅黑"
text: "I am yellow !"
color: "black"
}
}
MyItem2.qml
import QtQuick 2.0
Rectangle {
width: 640
height: 300
color: "red"
Text {
anchors.centerIn: parent
font.pixelSize: 48
font.family: "微軟雅黑"
text: "I am red !"
color: "black"
}
}
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
visible: true
width: 640
height: 480
property bool jud: true
property int count: 0
Loader {
id: loader
anchors.top: parent.top
anchors.left: parent.left
asynchronous: false // 異步加載,預設false
}
Text {
id: text
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 100
font.pixelSize: 24
font.family: "微軟雅黑"
color: "black"
text: JSON.stringify(count)
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 40
text: "load"
onClicked: {
timer1.start()
timer2.start()
}
}
Timer {
id: timer1
interval: 10
repeat: true
onTriggered: {
count++
}
}
Timer {
id: timer2
interval: 10
repeat: true
onTriggered: {
if (jud) {
loader.source = "MyItem1.qml"
} else {
loader.source = "MyItem2.qml"
}
jud = !jud
}
}
}
這裡說明一下,Loader 使用同步加載的方式在這個 Demo 中其實是不會卡頓的,Item 切換非常流暢,上圖gif中出現卡頓是因為錄制GIF的工具幀數沒有跟上。。。也是有點無語。小夥伴們可以自己跑一下程式看一下效果。
Loader 的 asynchronous 屬性代表是否異步加載,預設是false。當與源屬性一起使用時,加載和編譯也将在背景線程中執行。異步加載會跨多個幀建立元件聲明的對象,并減少動畫中出現故障的可能性。
異步加載時,狀态将更改為Loader.loading。建立整個元件後,該項将可用,狀态将更改為Loader.Ready。在進行異步加載時将此屬性的值更改為false将強制立即同步完成。這允許在異步加載完成之前必須通路加載程式内容時,開始異步加載,然後強制完成。
方法三:createObject / createQmlObject
MyItem.qml
import QtQuick 2.0
import QtQuick.Controls 2.5
Rectangle {
id: rec
property alias textInformation : myText.text
signal destroyMyself(var object)
Text {
id: myText
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 24
font.family: "微軟雅黑"
color: "black"
}
Button {
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
text: "delete"
onClicked: {
destroyMyself(rec)
}
}
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window {
visible: true
width: 1024
height: 768
Column {
id: column
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: 700
spacing: 5
}
Button {
id: createObjectBtn
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 120
anchors.bottomMargin: 50
text: "createObject"
onClicked: {
createObject()
}
}
Button {
id: createQmlObjectBtn
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: 120
anchors.bottomMargin: 50
text: "createQmlObject"
onClicked: {
createQmlObject()
}
}
Component {
id: myComponent
Rectangle {
id: rec
property alias textInformation : myText.text
signal destroyMyself(var object)
Text {
id: myText
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 24
font.family: "微軟雅黑"
color: "black"
}
Button {
anchors.right: parent.right
anchors.rightMargin: 10
anchors.verticalCenter: parent.verticalCenter
text: "delete"
onClicked: {
destroyMyself(rec)
}
}
}
}
function componentDestroy(object) {
object.destroy()
}
function createObject() {
// createComponent from external file "MyItem.qml"
var component = Qt.createComponent("MyItem.qml")
if (component.status === Component.Ready) {
var obj = component.createObject(column, { "color": "yellow",
"width": column.width,
"height": 50,
"textInformation": 'createComponent from external file "MyItem.qml"' })
obj.destroyMyself.connect(componentDestroy)
}
// createComponent from internal file myComponent
var obj2 = myComponent.createObject(column, { "color": "red",
"width": column.width,
"height": 50,
"textInformation": "createComponent from internal file myComponent" })
obj2.destroyMyself.connect(componentDestroy)
}
function createQmlObject() {
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "blue"; width: 1024; height: 50}',
column,
"");
}
}
object createQmlObject(qml, object parent, string filepath)
傳回從給定的qml字元串建立的具有指定父級的新對象,如果建立對象時出錯,則傳回null。 如果指定了filepath,它将用于為建立的對象報告錯誤。
檢視示例的代碼可以發現,通過 createQmlObject 動态建立不是很友善,因為如果元件的樣式或功能比較多的話,qml字元串就會很長很繁瑣。
object createObject(parent, object properties)
可以在檔案中或者在外部把元件定義好,如果是外部定義的需要先通過 createComponent 建立元件,然後再通過 createObject 建立此元件的對象執行個體。