# 跟踪动态对象

使用动态对象时,通常需要跟踪创建的对象。 另一个常见的特点是能够存储和恢复动态对象的状态。 使用动态填充的XmlListModel可以轻松地处理这两个任务。

在下面显示的示例中,用户可以创建和移动两种类型的元素:火箭和UFO。 为了能够操纵动态创建元素的整个场景,使用模型来跟踪这些项目。

在创建项目时填充模型,即XmlListModel。 对象引用与实例化时使用的源URL一起被跟踪。 源URL对于跟踪对象并不是严格需要的,但稍后会派上用场。

import QtQuick
import "create-object.js" as CreateObject

Item {
    id: root

    ListModel {
        id: objectsModel
    }

    function addUfo() {
        CreateObject.create("ufo.qml", root, itemAdded)
    }

    function addRocket() {
        CreateObject.create("rocket.qml", root, itemAdded)
    }

    function itemAdded(obj, source) {
        objectsModel.append({"obj": obj, "source": source})
    }

从上面的示例可以看出,create-object.js是前面介绍的JavaScript的更通用形式。 create方法有三个参数:源URL、根元素和完成时调用的回调函数。使用两个参数调用回调函数:对新创建对象的引用和使用的源URL。

这意味着每次调用addUfoaddRocket函数时,都会在创建新对象时调用itemAdded函数。itemAdded函数会将对象的引用和源URL附加到objectsModel模型上。

objectsModel可以以多种方式使用。在提及的示例中,clearItems函数依赖于它。这个函数演示了两件事:首先,如何迭代模型并执行任务,即为每个项目调用destroy函数以将其删除;其次,它强调了模型没有随着对象被销毁而更新的事实。该模型项的obj属性设置为null,而不是删除连接到相关对象的模型项。为了解决这个问题,代码必须在删除对象时明确清除模型项。

function clearItems() {
    while(objectsModel.count > 0) {
        objectsModel.get(0).obj.destroy()
        objectsModel.remove(0)
    }
}

拥有一个代表所有动态创建的项目的模型,很容易创建一个序列化项目的函数。 在示例代码中,序列化信息由每个对象的源URL及其xy属性组成,这些都是是用户可以更改的属性,这些信息用于构建XML文档字符串。

function serialize() {
    var res = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<scene>\n"

    for(var ii=0; ii < objectsModel.count; ++ii) {
        var i = objectsModel.get(ii)
        res += "  <item>\n    <source>" + i.source + "</source>\n    <x>" + i.obj.x + "</x>\n    <y>" + i.obj.y + "</y>\n  </item>\n"
    }

    res += "</scene>"

    return res
}

提示

目前,Qt 6的XmlListModel缺少序列化和反序列化工作所需的xml属性和get()函数。

通过设置XmlListModel模型的xml属性,可以将XML文档字符串与模型一起使用。 在下面的代码中,模型随着deserialize函数而显示。 deserialize函数通过将dsIndex设置为引用模型的第一个项目,然后调用该项目的创建来启动反序列化。 回调 dsItemAdded然后设置新创建对象的xy属性,然后它更新索引并创建下一个对象(如果有)。

XmlListModel {
    id: xmlModel
    query: "/scene/item"
    XmlListModelRole { name: "source"; elementName: "source" }
    XmlListModelRole { name: "x"; elementName: "x" }
    XmlListModelRole { name: "y"; elementName: "y" }
}

function deserialize() {
    dsIndex = 0
    CreateObject.create(xmlModel.get(dsIndex).source, root, dsItemAdded)
}

function dsItemAdded(obj, source) {
    itemAdded(obj, source)
    obj.x = xmlModel.get(dsIndex).x
    obj.y = xmlModel.get(dsIndex).y

    dsIndex++

    if (dsIndex < xmlModel.count) {
        CreateObject.create(xmlModel.get(dsIndex).source, root, dsItemAdded)
    }
}

property int dsIndex

该示例演示了如何使用模型来跟踪创建的项目,以及序列化和反序列化此类信息是多么容易。 这可用于存储动态填充的场景,例如一组小部件。 在示例中,使用模型来跟踪每个项目。

另一种解决方案是使用场景根的children属性来跟踪项目。 但是,这需要项目本身知道用于重新创建它们的源URL。 它还要求实现一种方法,能够将动态创建的项目与属于原始场景的项目区分开来,这样就可以避免尝试序列化和稍后反序列化任何原始项目。

最后更新: 1/28/2022, 7:37:27 PM