1. 程式人生 > >QML概念及框架--QML動態物件管理

QML概念及框架--QML動態物件管理

    QML提供了很多方法來動態建立和管理QML物件。如Loader、RePeater、ListView、GridView和PathView等元素都支援動態物件管理。物件也可以在C++中被建立和管理,這是混合QML/C++應用程式的首選方式。QML也支援在JavaScript程式碼中動態建立物件,這在現有的QML元素不適合應用程式需要的情況下是很有用的,而且也不需要涉及C++元件。

1. 動態建立物件

    這裡有兩種方法從JavaScript動態建立物件。既可以呼叫Qt.createComponent()來動態建立Compont物件,也可以使用Qt.createQmlObject()從QML字串來建立物件。如果已經有一個在.qml檔案中定義的元件,而且希望冬天建立該元件的一個例項,那麼使用第一種方法是比較好的;如果QML本身是在執行時產生的,那麼使用QML字串來建立物件是很有用的。

    (1) 動態建立一個元件

    要動態載入定義在一個QML檔案中的元件,可以在QML全域性物件上呼叫Qt.createComponent()函式。這個函式需要將QML檔案的URL作為其引數,然後這個URL上建立一個Component物件。一旦有了一個Component,就可以呼叫它的createObject()函式來建立該元件的一個例項。這個函式需要指定新物件的父物件。因為圖形專案沒有父物件是無法顯示在場景上的,所以建議這樣來設定父物件。然而,如果像稍後再為其設定父物件,可以安全的設定null作為該函式的引數。

    QML元件Sprite.qml

import QtQuick 2.4

Rectangle { width: 80; height: 50; color: "red" }
  匯入JavaScript檔案componentCreation.js來建立Sprite物件:
import QtQuick 2.4
import "componentCreation.js" as MyScript

Rectangle {
    id: appWindow
    width: 300; height: 300
    Component.onCompleted: MyScript.createSpriteObjects()

    componentCreation.js檔案,其中在呼叫createObject()以前檢查了元件的狀態是否為Component.Ready,因為如果QML檔案是從網路上載入的,那麼它不會立即可用:

var component;
var sprite;

function createSpriteObjects() {
    component = Qt.createComponent("Sprite.qml")
    if(component.status == Component.Ready)
        finishCreation();
    else
        component.statusChanged.connect(finishCreation);
}

function finishCreation() {
    if(component.status == Component.Ready) {
        sprite = component.createObject(appWidow)
        if(sprite == null) {
            //錯誤處理
        } else {
            sprite.x = 100
            sprite.y = 100
            //...
        }
    } else if(componet.status == Component.Error) {
        //錯誤處理
        console.log("Error loading component:", component.errorString())
    }
}
    如果可以確保QML檔案是從本地檔案載入的,那麼可以忽略finishCreation()函式,而在createSpriteObject()函式中立即呼叫createObject()函式,上面的可以寫成
function createSpriteObjects() {
    component Qt.createComponent("Sprite.qml")
    sprite = component.createObject(appWindow)
    if(sprite == null) {
        //Erro Handling
        console.log("Erro creating object");
    } else {
        sprite.x = 100
        sprite.y = 100
        //...
    }
}

    注意,這裡createObject()使用appWindow作為引數,所以建立的物件會成為main.qml中appWindow的子物件。當使用相對路徑來載入檔案時,需要是相對於執行Qt.createComponent()的檔案的路徑。將訊號關聯到 動態建立的物件上,或者從動態建立的物件上接收訊號,都要使用訊號的connect()函式。

    (2) 從QML字串建立一個物件

    如果QML直到執行時才被定義,可以使用Qt.createQmlObject()函式從一個QML字串建立一個QML物件。

var newObject = Qt.createQmlObject(
    'import QtQuick 2.4; Rectangle{color: "red"; width: 20; height:20}', parentItem, "dynamicSnippet1");
    第一個引數是要建立的QML字串,就像一個新的檔案一樣,需要匯入所需要的型別;第二個引數是父物件,需要在場景中已經存在的一個物件;第三個引數是於新物件相關檔案的路徑,用來報告錯誤。如果在QML字串中匯入的檔案使用的是相對路徑,那麼需要相對於定義父物件的檔案的路徑。

2. 文虎動態建立的物件

   當管理動態建立的物件是,必須確保建立上下文(creation context)不會再建立的物件銷燬前被銷燬。否則,如果建立上下文被首先銷燬,那麼在動態建立物件中的繫結將不會再工作。實際的建立上下文依賴於物件是怎樣被建立的:

    a.  如果使用了Qt.createComponent(),建立上下文就是呼叫該函式的QDeclarativeContext;

    b. 如果使用了Qt.crateQmlObject(),建立上下文就是父物件的上下文

    c. 如果定義了一個Component{},然後在其上呼叫了createObject(),建立上下文就是該Component中定義的上下文

    另外需注意,雖然動態建立的物件可以像其他物件一樣來使用,但是它們沒有id值。

3. 動態刪除物件

    在很多使用者介面中,將圖形項的透明度設定為0或者將其移除螢幕就夠了,而不需要將其刪除。然而,如果有很多動態生成的物件,難麼將不用的物件刪除會得到一個很大的效能提升。不過應該注意,永遠不要手動刪除通過QML元素(例如Loader和Repeater)動態生成的物件,不要刪除不是自己動態建立的物件。

    可以使用destroy()函式來刪除物件,這個函式有一個可選引數,可以用來設定在銷燬該物件以前的以毫秒為單位的延遲時間,該引數預設為0.在下面列子中,application.qml中建立了SelfDestroyingRect.qml元件的5個例項,每一個例項執行一個NumberAnimation,當動畫結束時在其根物件上呼叫destroy()來進行自我銷燬。

    application.qml檔案:

import QtQuick 2.4

Item {
    id: container
    width: 500; height: 100

    Component.onCompleted: {
        var component = Qt.createComponent("SelfDestroyingRect.qml")
        for(var i = 0; i < 5; i++) {
            var object = component.createObject(container)
            object.x = (object.width + 10) * i
        }
    }
}
    SelfDestroyingRect.qml檔案
import QtQuick 2.0<pre name="code" class="html">Item {
    SelfDestroyingRect{...}
}

Rectangle { id: rect width: 80; height: 80 color: "red" NumberAnimation on opacity { to: 0 duration: 1000 onRunningChanged: { if(! runnig) { console.log("Destroying ...") rect.destroy(); } } }}     另外,application.qml可以呼叫object.destroy()類銷燬建立的物件。在一個物件內部呼叫destroy()來銷燬自己是安全的。物件不會再destroy()被呼叫時就被銷燬,耳屎在該指令碼塊的末尾和下一幀之間的某個時間進行清理(除非將延時指定了一個非零值)。

     注意,如果SelfDestroyRect例項被靜態的建立

Item {
    SelfDestroyingRect{...}
}
   這樣會產生錯誤,只有動態建立的物件才可以被動態刪除。使用Qt.createQmlObject()建立的物件可以相似的使用destroy()來刪除
 var newObject = Qt.createQmlObject(
        'import QtQuick 2.4; Rectangle {color: "red"; width: 20; height:20}',
        parentItem, "dynamicSnippet1"
    );
    newObject.destroy(1000)