QML动态视图排序教程4 - 排序项目

拖放并非是唯一可用于在视图中调整项目顺序的方法。使用DelegateModel,还可以根据模型数据对项目进行排序。为此,我们需要像这样扩展我们的DelegateModel实例:

    DelegateModel {
        id: visualModel
        property var lessThan: [
            function(left, right) { return left.name < right.name },
            function(left, right) { return left.type < right.type },
            function(left, right) { return left.age < right.age },
            function(left, right) {
                if (left.size == "Small")
                    return true
                else if (right.size == "Small")
                    return false
                else if (left.size == "Medium")
                    return true
                else
                    return false
            }
        ]

        property int sortOrder: orderSelector.selectedIndex
        onSortOrderChanged: items.setGroups(0, items.count, "unsorted")

        function insertPosition(lessThan, item) {
            let lower = 0
            let upper = items.count
            while (lower < upper) {
                const middle = Math.floor(lower + (upper - lower) / 2)
                const result = lessThan(item.model, items.get(middle).model)
                if (result) {
                    upper = middle
                } else {
                    lower = middle + 1
                }
            }
            return lower
        }

        function sort(lessThan) {
            while (unsortedItems.count > 0) {
                const item = unsortedItems.get(0)
                const index = insertPosition(lessThan, item)

                item.groups = "items"
                items.move(item.itemsIndex, index)
            }
        }

        items.includeByDefault: false
        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
            onChanged: {
                if (visualModel.sortOrder == visualModel.lessThan.length)
                    setGroups(0, count, "items")
                else
                    visualModel.sort(visualModel.lessThan[visualModel.sortOrder])
            }
        }
        model: PetsModel {}
        delegate: dragDelegate
    }
环顾四周

DelegateModel中的项目将根据DelegateModelGroup类型分组,通常所有项目都默认属于一个名为items的组,但此默认值可以使用includeByDefault属性进行更改。为了实现排序,我们希望首先将项目添加到未排序组中,然后我们可以将其转移到项目组中特定的排序位置。为此,我们清除项目组上的includeByDefault,并设置名为'unsorted'的新组。

        items.includeByDefault: false
        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
        }

我们对项目进行排序的方式是首先找到要插入第一个未排序项目的项目组中的位置,然后将该项目移动到项目组中的预定索引处,然后重复此过程,直到未排序组为空。

要找到项目的插入位置,我们可以使用get()函数从未排序组请求项目的处理。通过此处理上的模型属性,我们可以访问在同一项目的代理实例中可用的相同模型数据,并将其与其他项目进行比较以确定相对位置。

        function insertPosition(lessThan, item) {
            let lower = 0
            let upper = items.count
            while (lower < upper) {
                const middle = Math.floor(lower + (upper - lower) / 2)
                const result = lessThan(item.model, items.get(middle).model)
                if (result) {
                    upper = middle
                } else {
                    lower = middle + 1
                }
            }
            return lower
        }

        function sort(lessThan) {
            while (unsortedItems.count > 0) {
                const item = unsortedItems.get(0)
                const index = insertPosition(lessThan, item)

                item.groups = "items"
                items.move(item.itemsIndex, index)
            }
        }

排序函数的lessThan参数是一个比较函数,它将确定列表的顺序。在这个例子中,它可以是以下之一

        property var lessThan: [
            function(left, right) { return left.name < right.name },
            function(left, right) { return left.type < right.type },
            function(left, right) { return left.age < right.age },
            function(left, right) {
                if (left.size == "Small")
                    return true
                else if (right.size == "Small")
                    return false
                else if (left.size == "Medium")
                    return true
                else
                    return false
            }
        ]

每当新项目添加到我们未被排序的DelegateModel时,都会触发排序,我们通过DelegateModelGrouponChanged处理程序通知。如果没有当前选择排序函数,我们只需将未排序组中的所有项目移动到项目组中,否则我们使用所选排序函数调用sort。

        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
            onChanged: {
                if (visualModel.sortOrder == visualModel.lessThan.length)
                    setGroups(0, count, "items")
                else
                    visualModel.sort(visualModel.lessThan[visualModel.sortOrder])
            }
        }

最后,当选择的排序顺序发生变化时,我们可以通过将所有项目从项目组移动到未排序组来触发列表的完整排序,这将触发DelegateModelGrouponChanged处理程序并按正确顺序重新将项目转移到项目组中。请注意,DelegateModelGrouponChanged处理程序不会递归调用,因此在排序期间调用它不会出现问题。

        property int sortOrder: orderSelector.selectedIndex
        onSortOrderChanged: items.setGroups(0, items.count, "unsorted")

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd. 本文档中的贡献内容为各自所有者的版权。本文档依据由自由软件基金会发布的GNU自由文档许可证第1.3版许可条款提供。Qt及其相关标志是芬兰及/或其他国家/地区的The Qt Company Ltd.的商标。所有其他商标均为各自所有者的财产。