本文介绍如何在 QML 中使用 JavaScript 动态创建和销毁对象。主要涵盖通过 [[wp:a0]] 和 [[wp:a1]] 两种动态创建方式,以及利用 [[wp:a12]] 方法安全移除实例。文章详细解析了参数语义、静态构建注意事项及性能优化建议,帮助开发者掌握 QML 对象生命周期管理。
一、动态创建对象
在 QML 中,可以通过 JavaScript 实现对象的动态生成,主要包含以下两种途径:
- 调用
Qt.createComponent()动态创建 Component 对象 - 使用
Qt.createQmlObject()从 QML 字符串创建对象
需要注意的是,通过动态方式创建的对象虽然可以像常规对象一样使用,但它们不会在 QML 系统中分配 id。
1.1、动态创建组件
可以通过调用组件的 createObject() 方法来生成该组件的一个具体实例。该方法支持接收两个参数:
第一个参数用于指定新对象的父对象。父对象可以是图形元素(继承自 Item 类型)或非图形元素(如 QtObject 或 C++ QObject 类型)。只有拥有图形父对象的图形对象才会被渲染到 Qt Quick 可视画布上。若计划稍后设置父级关系,可以向此函数传递 null。第二个参数是可选的属性值映射表,用于定义对象的初始属性。在对象创建完成前应用这些属性值,能有效避免因特定属性未初始化而引发的绑定错误。此外,相较于创建后单独设置属性和绑定,这种方式带来的性能提升微乎其微。
以下是一个简单的 QML 组件定义示例:
import QtQuick 2.0
Rectangle { width: 80; height: 50; color: "red" }
在 main.qml 中导入了一个名为 componentCreation.js 的 JavaScript 文件,该文件负责创建 Sprite 对象:
import QtQuick 2.0
import "componentCreation.js" as MyScript
Rectangle
{
id: appWindow
width: 300; height: 300
Component.onCompleted: MyScript.createSpriteObjects();
}
componentCreation.js
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)
{
console.log("Error creating object");
}
}
else if (component.status == Component.Error)
{
console.log("Error loading component:", component.errorString());
}
}
当使用相对路径引用文件时,该路径应相对于执行 Qt.createComponent() 的文件位置。
此外,还可以利用 bateObject() 函数在不阻塞主线程的情况下实例化组件。
1.2、从 QML 字符串创建对象
利用 Qt.createQmlObject() 函数,可以直接从 QML 字符串构建 QML 对象,具体用法如下例所示:
const newObject = Qt.createQmlObject(`
import QtQuick 2.0
Rectangle
{
color: "red"
width: 20
height: 20
}
`,
parentItem,
"myDynamicSnippet"
);
- 第一个参数是需要创建的 QML 字符串内容。
- 第二个参数指定新对象的父对象,其语义与组件的父参数一致,同样适用于
createQmlObject()。 - 第三个参数是与新对象关联的文件路径,主要用于错误信息的报告。
若 QML 字符串中使用了相对路径来导入文件,则该路径应相对于定义父对象(即方法的第二个参数)的文件位置。
在构建静态 QML 应用程序时,系统会扫描 QML 文件以检测导入依赖项,从而在编译时解决所有必要的插件和资源。然而,这一机制仅处理显式导入语句(位于 QML 文件顶部的语句),并不包含字符串文字内部的导入语句。为了支持静态构建,用户需确保使用 Qt.createQmlObject() 的 QML 文件在文件顶部明确包含所有必要的导入,包括那些位于字符串文字内部的导入。
二、动态删除对象
在许多用户界面场景中,将可视对象的不透明度设置为 0 或将其移出屏幕往往足以满足需求。然而,当存在大量动态创建的对象时,及时删除未使用的对象能带来显著的性能优势。
特别注意:严禁手动删除由 QML 对象工厂(如 Loader 和 Repeater)动态创建的对象。同时,也应避免删除非自身动态创建的对象。
可以使用 destroy() 方法来移除项目。该方法包含一个可选参数(默认值为 0),用于指定销毁对象前的近似延迟时间(毫秒)。
示例:application.qml 创建了 SelfDestroyingRect.qml 组件的五个实例。每个实例执行一个 NumberAnimation,当动画结束时,调用其根对象上的 destroy() 以销毁自身:
application.qml
import QtQuick 2.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 2.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() 的瞬间立即销毁,而是在当前脚本块执行完毕与下一帧渲染之间的某个时刻被清除(除非指定了非零延迟)。
另外,如果 SelfDestroyingRect 实例是像这样静态创建的:
Item
{
SelfDestroyingRect
{
// ...
}
}
这将导致错误,因为对象仅在动态创建时才支持动态销毁。
通过 Qt.createQmlObject() 创建的对象也可以类似地使用 destroy() 进行销毁:
const newObject = Qt.createQmlObject(`
import QtQuick 2.0
Rectangle
{
color: "red"
width: 20
height: 20
}
`,
parentItem,
"myDynamicSnippet"
);
newObject.destroy(1000);
至此,关于 JavaScript 中创建动态 QML 对象的方法介绍完毕。如需了解更多 JavaScript 创建动态 QML 对象的内容,请搜索本站历史文章或继续浏览后续相关文章。