现在的位置: 首页 > 综合 > 正文

在QML中管理动态对象

2013年03月26日 ⁄ 综合 ⁄ 共 4326字 ⁄ 字号 评论关闭

原文地址:http://doc.qt.digia.com/4.7-snapshot/qdeclarativedynamicobjects.html

QML提供了很多种方式动态创建和管理QML对象.Loader,Repeater,ListView,GridView和PathView都支持动态对象管理.对象也可在C++中创建并管理,这是QML\C++相结合的应用程序首选方法.

QML也支持在Javascript代码中动态创建对象.这在当前QML文件无法满足需要是很有用,而且不会卷入额外的C++组件.

本文中下面的动态场景范例展示了这个概念.

动态创建对象

在JavaScript中动态创建对象有两种方式.可调用Qt.createComponent()动态创建一个组件对象,或使用Qt.createQmlObject()从一个描述QML的字符串创建元素.最好是有一个定义在.qml文件中的组件,需要动态创建这个组件的实例.否则,就需要在运行时使用一个QML描述字符串创建元素.

动态创建组件

要动态加载QML文件中的组件,可在QML全局对象中调用Qt.createComponent()函数.这个函数只有一个指向QML文件的URL参数并创建这个URL指向的组件对象.
创建了组件后,可以调用其createObject()方法创建组件的实例.这个函数有一个或两个参数:
  第一个参数是新元素的parent.由于图形项(graphical item)在场景中必须有parent,否则就无法显示,还是推荐使用这种方式设置一个parent.然而,如果希望稍后在设置可以传递一个null实参.

  第二个参数是可选的属性名称-值的映射列表,用来初始化元素的属性值.这个参数指定的属性值在对象构造完成之前被赋给对象的属性,避免了有些属性在用于绑定前必须被初始化而发生的错误.在属性用于绑定前就已经设置了值.另外,与对象创建后再设置属性值和绑定相比,具有稍微的效率优势.   

下面是一个范例,首先定义Sprite.qml文件:

 import QtQuick 1.0

 Rectangle { width: 80; height: 50; color: "red" }

主应用程序文件是main.qml,导入可创建Spite对象的componentCreation.js JavaScript文件:

 import QtQuick 1.0
 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(appWindow, {"x": 100, "y": 100});
         if (sprite == null) {
             // Error Handling
             console.log("Error creating object");
         }
     } else if (component.status == Component.Error) {
         // Error Handling
         console.log("Error loading component:", component.errorString());
     }
 }

如果从本地文件加载QML文件,可以忽略finishCreation()函数直接调用createObject():

 function createSpriteObjects() {
     component = Qt.createComponent("Sprite.qml");
     sprite = component.createObject(appWindow, {"x": 100, "y": 100});

     if (sprite == null) {
         // Error Handling
         console.log("Error creating object");
     }
 }

注意这两个实例中,createObject()函数的第一个参数是appWindow,使新创建的对象作为main.qml中的appWindow对象的子对象.否则,新创建的对象不会在场景中显示.
当用相对路径引用文件时,路径是相对于执行Qt.CreatComponent()函数的文件而言的.

要连接动态创建对象上的信号(或槽),可使用信号的connect()方法.

基于QML的字符串创建对象

如果在运行时还没有建立QML文件,可以使用Qt.CreateQmlObject()函数使用QML描述字符串来创建QML对象,如下所示:

 var newObject = Qt.createQmlObject('import QtQuick 1.0; Rectangle {color: "red"; width: 20; height: 20}',
     parentItem, "dynamicSnippet1");

第一个参数是创建QML的字符串.与创建新的QML文件一样,需要导入必要的类型.第二个参数是新对象的parent;这个parent必须在场景中存在.第三个参数新对象相关的文件连接,用于导出错误报表.
如果QML中使用相对路径导入文件,路径是相对于定义了parent对象的文件而言的.

维护动态创建的对象

要管理动态创建的对象,必须要确保创建上下文的生命周期比被创建的对象长.否则当创建上下文被释放后,对动态对象的绑定不在生效.
事实上创建上下文依赖于对象是如何创建的:

    如果使用Qt.createComponent(),创建上下文是调用这个方法的QDeclarativeContext
    如果调用Qt.createQmlObject(),创建上下文是传递给这个函数的parent参数的上下文
    如果定义了Component{} 对象并在其上调用了createObject(),创建上下文是定义的那个Component

同时主要动态创建对象可像其他对象一样使用,在QML中没有id属性.

删除动态对象

在很多用户接口中,将对象的opacity设置为0或将其移动到屏幕之外,而不是直接删除对象.如果创建了很多动态对象,删除无用的对象会获得显著的效率提升.
主要不要手动删除由QML元素动态创建的对象(如Loader和Repeater).同时,避免删除不是由你创建的动态对象.
对象可调用destroy()方法删除.这个方法有一个可选参数(默认值为0)指定对象被删除的大概延时(毫秒).
下面是一个范例.application.qml创建了五个SelfDestroyingRect.qml组件.每个实例都会运行一个NumberAnimation动画,动画完成后,在其根对象上调用destroy()方法释放自己:
application.qml   

 import QtQuick 1.0

 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 1.0

 Rectangle {
     id: rect
     width: 80; height: 80
     color: "red"

     NumberAnimation on opacity {
         to: 0
         duration: 1000

         onRunningChanged: {
             if (!running) {
                 console.log("Destroying...")
                 rect.destroy();
             }
         }
     }
 }

也可以在application.qml中调用object.destroy()删除其创建的对象.
注意在一个对象上调用destroy()是安全的.在对象上调用destroy()不会马上释放,而是在脚本阻塞后或下一帧时执行清理(除非设置了非0的延时).
注意如果SelfDestroyRect如下方式创建:

 Item {
     SelfDestroyingRect {
         // ...
     }
 }

由于只能动态释放动态创建的对象,这时会发生错误.

使用Qt.createQmlObject()创建的对象也同样使用destroy()释放:

 var newObject = Qt.createQmlObject('import QtQuick 1.0; Rectangle {color: "red"; width: 20; height: 20}',
     parentItem, "dynamicSnippet1");
 newObject.destroy(1000);

抱歉!评论已关闭.