QML支持在JavaScript中动态创建对象。这有助于将对象的实例化推迟到必要的时候,从而提高应用程序的启动时间。它还允许动态创建可视对象,并根据用户输入或其他事件将其添加到场景中。动态创建用的场景是非常的多。有些组件在时间线上不是同时创建的,同时就像弹窗一样,有些组件其实是在运行的时候创建一会儿,就销毁掉了。
在JavaScript中动态创建对象有两种方法。
这两种方法有不同的适用范围,如果有在QML文档中定义的现有组件,并且希望动态创建该组件的实例,则创建组件是更好的选择。否则,当对象QML本身在运行时生成时,从QML字符串创建对象是有用的。
要动态加载QML文件中定义的组件,在Qt对象中调用Qt. createcomponent()函数。这个函数将QML文件的URL作为唯一的参数,并从这个URL创建一个组件对象。
一旦有了一个组件,就可以调用它的 createObject() 方法来创建该组件的实例。这个函数可以接受一个或两个参数:
这里有一个例子。首先是 Sprite.qml 定义了一个简单的qml组件:
import QtQuick 2.0Rectangle { width: 80; height: 50; color: "red" }
我们的主应用程序文件 main.qml 导入一个 componentcreate .js JavaScript文件,用来创建子画面 Sprite 对象:
import QtQuick 2.0
import "componentCreation.js" as MyScriptRectangle {id: appWindowwidth: 300; height: 300Component.onCompleted: MyScript.createSpriteObjects();
}
下面是componentcreate .js。注意它检查组件状态是否为component。在调用createObject()之前准备好,以防QML文件通过网络加载,因此不能立即准备好。
var component;var sprite;function createSpriteObjects() {component = Qt.createComponent("Sprite.qml");if (component.status == Component.Ready)finishCreation();elsecomponent.statusChanged.connect(finishCreation);}function finishCreation() {if (component.status == Component.Ready) {sprite = component.createObject(appWindow, {x: 100, y: 100});if (sprite == null) {// Error Handlingconsole.log("Error creating object");}} else if (component.status == Component.Error) {// Error Handlingconsole.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 Handlingconsole.log("Error creating object");}}
注意,在这两个例子中,调用 createObject() 时都将 appWindow 作为父参数,因为动态创建的对象是一个可视化(Qt Quick)对象。创建的对象将成为 main.qml 中的 appWindow对象的一个子对象,并出现在场景中。
当使用具有相对路径的文件时,路径应该相对于Qt.createComponent()执行的文件。 路径都是相对于 有这个 Qt.createComponent() 函数的文件位置
要将信号连接到(或从)动态创建的对象中接收信号,可以使用signal connect()方法。通过incubateObject()函数也可以实例化组件而不会阻塞。为该组件创建实例。这个函数的创建过程允许异步实例化新的组件实例,并且不会导致UI冻结。
var component = Qt.createComponent("Button.qml");var incubator = component.incubateObject(parent, { x: 10, y: 10 });if (incubator.status != Component.Ready) {incubator.onStatusChanged = function(status) {if (status == Component.Ready) {print ("Object", incubator.object, "is now ready!");}}} else {print ("Object", incubator.object, "is ready immediately!");}
如果QML在运行时才定义,可以使用Qt.createQmlObject()函数从QML字符串创建QML对象,如下面的例子所示:
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',parentItem,"dynamicSnippet1");
如果QML字符串使用相对路径导入文件,则该路径应该相对于定义父对象(方法的第二个参数)的文件。
重要提示:在构建静态QML应用程序时,将扫描QML文件以检测导入依赖关系。这样,所有必要的插件和资源都在编译时解决。但是,只考虑显式的import语句(在QML文件顶部的那些语句),而不考虑包含在字符串字面量中的import语句。为了支持静态构建,因此需要确保QML文件使用Qt.createQmlObject(),在文件的顶部显式地包含所有必要的导入,除了在字符串字面量内部。也就是说 要明确的 写好 'import QtQuick 2.0;’ 这个部分的东西。
在管理动态创建的对象时,必须确保创建上下文的寿命比创建的对象长。否则,如果创建上下文首先被销毁,动态对象中的绑定和信号处理程序将不再工作。也就是说这个 创建这个动态对象的所在对象必须寿命要比这个创建对象的长,这个才符号逻辑。
实际的创建上下文取决于对象是如何创建的:
另外,请注意,虽然动态创建的对象可以与其他对象一样使用,但它们在QML中没有id。
在许多用户界面中,只要将可视对象的不透明度设置为0,或者将其移出屏幕而不是删除就足够了。但是,如果你有很多动态创建的对象,如果删除未使用的对象,你可能会获得值得的性能提升。
请注意,永远不要手动删除由便利的QML对象工厂动态创建的对象(如Loader和Repeater)。此外,应该避免删除不是自己动态创建的对象。
可以使用destroy()方法删除元素。这个方法有一个可选参数(默认值为0),指定对象被销毁前的近似延迟时间(以毫秒为单位)。
这里有一个例子。application.qml 创建了 SelfDestroyingRect.qml 的五个实例。每个实例都会运行一个NumberAnimation,动画结束后,调用根对象的 destroy() 方法销毁自己:
application.qml
import QtQuick 2.0Item {id: containerwidth: 500; height: 100Component.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.0Rectangle {id: rectwidth: 80; height: 80color: "red"NumberAnimation on opacity {to: 0duration: 4000onRunningChanged: {if (!running) {console.log("Destroying...")rect.destroy();}}}}
或者,application.qml 可以通过调用 object.destroy() 来销毁创建的对象。
注意,在该对象内的对象上调用destroy()是安全的。对象不会在destroy()被调用的瞬间被销毁,而是会在脚本块结束和下一帧之间的某个时间被清理(除非你指定了一个非零的延迟)。
还要注意,如果像下面这样静态地创建一个SelfDestroyingRect实例:
Item {SelfDestroyingRect {// ...}}
这将导致错误,因为只有动态创建的对象才能被动态销毁。
用Qt.createQmlObject()创建的对象同样可以使用destroy()来销毁:
var newObject = Qt.createQmlObject('import QtQuick 2.0; Rectangle {color: "red"; width: 20; height: 20}',parentItem,"dynamicSnippet1");
newObject.destroy(1000);