【问题标题】:ListView's itemAtIndex does not return items at indexListView 的 itemAtIndex 不返回索引处的项目
【发布时间】:2020-09-12 04:17:29
【问题描述】:

假设我们有一个模型和两个视图。两个视图使用相同的模型。第二个视图中的项目需要与第一个视图中的相应项目具有相同的宽度。项目之间的宽度不是恒定的。两个视图之间的项目具有相同的索引。

下面的代码生成了两个带有索引的矩形系列作为文本。 view2 的矩形更窄:

import QtQuick 2.15

Rectangle {
    ListView {
        id: view1
        y: 0
        height: 50
        width: parent.width

        model: ts_model
        delegate: Rectangle {
            width: 60
            height: 50
            color: "black"
            Text {
                text: index
                color: "white"
            }
        }

        orientation: Qt.Horizontal
        layoutDirection: "LeftToRight"
        spacing: 5
    }

    ListView {
        id: view2
        y: 100
        height: 50
        width: parent.width

        model: ts_model
        delegate: Rectangle {
            color: "yellow"
            width: 50
            height: 50
            Text {
                text: index
            }
        }

        orientation: Qt.Horizontal
        layoutDirection: "LeftToRight"
        spacing: 5
    }
}

让我们尝试将来自view1 的矩形宽度绑定到view2 中的矩形。我希望能够使用view1.itemAtIndex(index) 语法来做到这一点:

ListView {
        id: view2
        y: 100
        height: 50
        width: parent.width

        model: ts_model
        delegate: Rectangle {
            color: "yellow"
            width: view1.itemAtIndex(index).width
            height: 50
            Text {
                text: index
            }
        }
(... some more code ...)

不幸的是,这不起作用 - 我得到 TypeError: Cannot read property 'width' of null 有趣的是,它仅对某些项目失败:

我证实:

  1. 两个视图显示相同数量的元素(相同模型,无过滤)
  2. 两者的索引都从 0 开始,直到模型中的元素数减一(如两张图片所示)
  3. 两个索引都是整数
  4. view1view2 是同一个父级的子级,我验证他们在通过 id 寻址时可以看到对方

为什么它不起作用?

【问题讨论】:

  • 在您调用itemAtIndex 时,view1 中的项目可能尚未完全构建。以后调用还行吗?
  • @JarMan 好点,虽然我不确定如何确保稍后发生调用
  • 例如在窗口的Component.onCompleted 处理程序中调用它。
  • “项目之间的宽度不固定” 为什么?宽度取决于什么?调用itemAtIndex 是一种代码味道,看起来对你来说宽度是模型状态的一部分,不要盯着代表的状态。您需要两个列表独立滚动吗?如果不是,难道你不能同时在一个视图中垂直堆叠两个代表吗?
  • @GrecKo 一个项目可以有多个孩子,这些孩子将根据用户请求显示/隐藏,从而改变特定项目的宽度。第二个视图中的相应项目必须更改其宽度以匹配第一个项目,该项目已因用户操作而更改。

标签: qt qml qtquick2


【解决方案1】:

这个答案是错误的。它仅适用于所有元素都可见的列表。当代表进入/离开范围时,这就会分崩离析。

但是,要回答最初的问题 - itemAtIndex 始终没有返回项目的原因是由于构造顺序,即在调用方法时索引处的项目没有完全构造。

正如 JarMan 所建议的,问题与 view1 的子结构有关。这是通过一个简单的测试来揭示的。我添加了一条消息来标识矩形的创建顺序:

// view1
delegate: Rectangle {
            width: 60
            height: 50
            color: "black"
            Text {
                text: index
                color: "white"
                Component.onCompleted: console.log("Rect1 " + index + " completed")
            }
        }
// view2
delegate: Rectangle {
                color: "yellow"
                width: view1.itemAtIndex(index).width
                height: 50
                Text {
                    text: index
                }
                Component.onCompleted: console.log("Rect2 " + index + " completed")
            }

产生以下输出:

qml: Rect2 0 completed
qml: Rect1 0 completed
file:///...: TypeError: Cannot read property 'width' of null
qml: Rect1 1 completed
qml: Rect1 2 completed
qml: Rect1 3 completed
qml: Rect1 4 completed
qml: Rect2 1 completed
qml: Rect2 2 completed
qml: Rect2 3 completed
qml: Rect2 4 completed
qml: Rect2 5 completed
qml: Rect2 6 completed
qml: Rect2 7 completed
qml: Rect2 8 completed
qml: Rect2 9 completed
file:///...: TypeError: Cannot read property 'width' of null
file:///...: TypeError: Cannot read property 'width' of null
file:///...: TypeError: Cannot read property 'width' of null
file:///...: TypeError: Cannot read property 'width' of null
file:///...: TypeError: Cannot read property 'width' of null
qml: Rect1 5 completed
qml: Rect1 6 completed
qml: Rect1 7 completed

很明显,view2 中未正确创建的矩形是在 view1 对应对象可用之前创建的。

动态对象创建覆盖here

解决此问题的一种方法是 1) 将第二个视图保存到单独的 QML 文件中 2) 在根项完成时将其创建为子项:

view2's QML file

import QtQuick 2.0

ListView {
    id: view2
    y: 100
    height: 50
    width: parent.width

    model: ts_model
    delegate: Rectangle {
        color: "yellow"
        width: view1.itemAtIndex(index).width
        height: 50
        Text {
            text: index
        }
    }

    orientation: Qt.Horizontal
    layoutDirection: "LeftToRight"
    spacing: 5
}

main file

import QtQuick 2.15

Rectangle {
    id: rect
    Component.onCompleted: {
        Qt.createComponent("view2.qml").createObject(rect)
    }

    ListView {
        id: view1
        y: 0
        height: 50
        width: parent.width

        model: ts_model
        delegate: Rectangle {
            width: 60
            height: 50
            color: "black"
            Text {
                text: index
                color: "white"
            }
        }

        orientation: Qt.Horizontal
        layoutDirection: "LeftToRight"
        spacing: 5
    }
}

这会产生预期的结果:

【讨论】:

  • 我不相信这个解决方案是可行的,委托并不总是在开始时被实例化,例如如果有太多元素无法容纳在整个视图中。
  • @GrecKo 是否有助于两个视图同步,即只有 view2 中的可见元素会请求 view1 元素的宽度,由于同步,这些元素也将可见?
  • @GrecKo 我只是根据您的观点对其进行了测试,您是正确的,需要滚动的列表更大,空指针问题是个问题。
猜你喜欢
  • 2019-08-13
  • 1970-01-01
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多