【QT】QML如何建立動態元件
QML動態元件指的是按需分配,需要時我們就建立一個自定義元件,也就是所謂的物件延遲例項化,而不是在程式一開始就建立它,不需要時我們就把它銷燬以節約記憶體,而不是傳統意義上的隱藏或覆蓋。我們可以使用Component與Loader,也可以使用JavaScript的形式來完成。
先來介紹一下Component——
progress屬性,載入元件的過程,從0.0到1.0變化。
status屬性,載入元件的狀態,其列舉值分別是Component.Null/Ready/Loading/Error。
url屬性,元件路徑。
completed()附加訊號,物件例項化完成後觸發。
destruction()附加訊號,物件開始銷燬時觸發。
object createObject(Itemparent, object properties)函式,建立物件。
string errorString()函式,錯誤描述。
object incubateObject(Itemparent, object properties,enumerationmode)函式,通過mode引數來非同步或同步建立物件,mode的值可以是Qt.ASynchronous/Synchronous,預設為非同步,返回值的屬性有status、object、onStatusChanged、forceCompletion(),其中
forceCompletion()函式強制同步建立物件。
再來看一下Loader——
active屬性,預設為true,設定成false時將不能載入元件。
asynchronous屬性,預設為false,設定成true時非同步載入元件。
item屬性,只讀屬性,儲存了成功載入的元件。
progress屬性,只讀屬性,從0.0到1.0變化。
source屬性,載入的元件是一個獨立的QML檔案。
sourceComponent屬性,在同一個QML檔案中載入元件。
status屬性,載入元件的狀態,其列舉值分別是Loader.Null/Ready/Loading/Error。
loaded()訊號,元件成功載入時觸發。
object setSource(urlsource, object properties
在同一個QML檔案中使用Component和Loader——
Component可封裝我們想要的東西,對外只提供一個定義好的介面,也就是其id屬性,然後我們就可以重複使用它了。如果某個QML檔案重複使用的Component比較小,或者說Component在邏輯上屬於某個QML檔案,那麼該Component就應該在這個QML檔案中定義,此時的Loader用到的就是其sourceComponent屬性,如下例子:
import QtQuick 2.2
Rectangle {
width: 360; height: 360
color: "lightblue"
MouseArea {
anchors.fill: parent
onClicked: {
loader.sourceComponent = component
loader2.sourceComponent = component
}
}
Component {
id: component
Rectangle {
width: 80; height: 80
color: "red"
}
}
Loader { id: loader }
Loader {
id: loader2;
anchors.centerIn: parent
onLoaded: item.color = "green"
}
}
由於Component不是繼承自Item,所以使用anchors錨佈局無效,但Loader可以使用。例子中單擊滑鼠時建立了兩個元件,雙擊滑鼠時又把這兩個元件銷燬了,銷燬時需要設定sourceComponent屬性值為undefined。
元件分離——
QML檔案本身也可以是一個Component,這樣就與使用它的QML檔案分離開了,這樣做的好處是該Component可以被多個QML檔案使用,程式碼結構也清晰明瞭,此時的Loader用到的就是其source屬性,如下例子:
// comp.qml as a separated Component
import QtQuick 2.2
Item {
Row {
spacing: 5
Rectangle {width: 80; height: 80; color: "red" }
Rectangle {width: 80; height: 80; color: "yellow" }
Rectangle {width: 80; height: 80; color: "green" }
}
}
// main.qml
import QtQuick 2.2
Rectangle {
width: 360; height: 360
color: "lightblue"
MouseArea {
anchors.fill: parent
onClicked: {
loader.source = "comp.qml"
loader2.source = "comp.qml"
}
onDoubleClicked: {
loader.source = ""
loader2.source = ""
}
}
Loader { id: loader}
Loader { id: loader2; y: 100}
}
例子中單擊滑鼠時從外部載入了兩個元件,雙擊滑鼠時又把這兩個元件銷燬了,銷燬時需要設定source屬性值為“”即一個空值。
從QML檔案中建立——
先使用Qt.createComponent(url, mode, parent)從QML檔案中建立一個元件,必要時可以根據Component.status屬性判斷建立狀態,然後使用Component.createObject()在某個父物件下例項化物件,最後使用destroy()銷燬物件,函式引數可以指定一個時間,單位是毫秒,預設為0,如下例子:
import QtQuick 2.2
Rectangle {
property var object
property var component
width: 360; height: 360
color: "lightblue"
MouseArea {
anchors.fill: parent
onPressed: {
component = Qt.createComponent("comp.qml")
if(Component.Ready === component.status) {
object = component.createObject(parent)
}
}
onReleased: {
object.destroy(2000)
}
}
}
例子中按下滑鼠時從QML檔案中建立了一個元件component並例項化一個物件object,釋放滑鼠時在2000毫秒後物件object銷燬,但元件component還是存在的。
從QML字串中建立——
使用Qt.createQmlObject(stringqml, object parent, string filepath)從QML字串中建立,第一個引數是要建立物件的QML字串,第二個引數指定要建立物件的父物件,第三個引數用於報告錯誤,例子如下:
import QtQuick 2.2
Rectangle {
property var object
width: 360; height: 360
color: "lightblue"
MouseArea {
anchors.fill: parent
onPressed: object = Qt.createQmlObject('import QtQuick 2.2; Rectangle { color: "red"; width: 100; height: 100; anchors.centerIn: parent }', parent, "dynamicSnippet")
onReleased: object.destroy(1000)
}
}
如果修改例子中Qt.creatQmlObject()的第一個引數的“color”為“colo”,程式執行時將會報錯,這時第三個引數就派上用場了,且看錯誤提示如下:
Error: Qt.createQmlObject(): failed to create object:
qrc:///dynamicSnippet:1:33: Cannot assign to non-existent property "colo"
這裡共列舉了動態元件建立與銷燬的四種方法,實際使用過程中可按需選擇。