QML ListView:检测到 属性 "height" 的绑定循环

QML ListView: Binding loop detected for property "height"

我有一个 QML ListView,我正在尝试向其中动态添加元素。我希望背景矩形也可以动态缩放,因为元素是来自 ListView 的 added/removed。现在我得到了一个绑定循环,我知道它们是什么,但我不知道它是从哪里来的。我尝试稍微更改一下代码,一次能够摆脱绑定循环,但随后无法滚动 ListView。有人有什么想法吗?

import QtQuick 2.15
import QtQuick.Window 2.0

Window {
  visible: true
  width: 800
  height: 800

      Rectangle {
          id: listContainer
          height: childrenRect.height
          width: parent.width
          color: "transparent"
          anchors {
              top: parent.top
              topMargin: 30
              left: parent.left
              leftMargin: 45
          }

          ListView {
              anchors.top: parent.top
              anchors.left: parent.left
              anchors.right: parent.right
              model: myModel
              height: childrenRect.height
              header:
                  Text {
                    z: 2
                    height: 50
                    text: "HEADER"
                    color: "black"

              }
              delegate:  Component {
                  Item {
                      Text {
                          id:  userName;
                          text: name;
                          color: "black";
                          font.pixelSize: 50
                          anchors {
                              left: parent.left
                              leftMargin: 20
                          }
                      }
                      
                      Rectangle {
                          height: 1
                          color: 'black'
                          width: listContainer.width
                          anchors {
                              left:  userName.left
                              top:  userName.top
                              topMargin: - 12
                              leftMargin: -15
                          }
                      }
                  }
              }
              spacing: 80
          }
      }

      ListModel {
          id: myModel
      }

      /* Fill the model with default values on startup */
      Component.onCompleted: {
          for (var i = 0; i < 100; i++) {
              myModel.append({
                  name: "Big Animal : " + i
              })
          }
      }
}

编辑:正如@Aditya 所建议的,可以通过具有静态 ListView 高度来删除绑定循环,但我不希望它是那样的。我使用矩形作为 ListView 的背景,我希望它根据 ListView 进行缩放。例如,如果我只添加两个元素,我希望矩形也针对这两个元素进行缩放,而不是覆盖整个屏幕。这导致了一个问题:

import QtQuick 2.15
import QtQuick.Window 2.0

Window {
  visible: true
  width: 800
  height: 800

      Rectangle {
          id: listContainer
          height: childrenRect.height
          width: parent.width
          color: "yellow"
          anchors {
              top: parent.top
              topMargin: 30
              left: parent.left
              leftMargin: 45
          }

          ListView {
              anchors.top: parent.top
              anchors.left: parent.left
              anchors.right: parent.right
              model: myModel
              height: 800//childrenRect.height

              header:
                  Text {
                    z: 2
                    height: 50
                    text: "HEADER"
                    color: "black"

              }
              delegate:  Component {
                  Item {
                      Text {
                          id:  userName;
                          text: name;
                          color: "black";
                          font.pixelSize: 50
                          anchors {
                              left: parent.left
                              leftMargin: 20
                          }
                      }

                      Rectangle {
                          height: 1
                          color: 'black'
                          width: listContainer.width
                          anchors {
                              left:  userName.left
                              top:  userName.top
                              topMargin: - 12
                              leftMargin: -15
                          }
                      }
                  }
              }
              spacing: 80
          }
      }

      ListModel {
          id: myModel
      }

      /* Fill the model with default values on startup */
      Component.onCompleted: {
          for (var i = 0; i < 2; i++) {
              myModel.append({
                  name: "Big Animal : " + i
              })
          }
      }
}

我还尝试将 header 从 ListView 分离到一个不同的组件中,并将列表视图固定在它下面并且成功了。唯一的问题是它不能用列表视图滚动。最坏的情况是,我可以为它制作一个滚动动画,但这似乎是一个低效的解决方案,我想知道为什么这不起作用。

绑定循环源自 ListView 的 height: childrenRect.height 语句。看起来 ListView 需要固定高度,或者至少不依赖于 childrenRect。这很可能是 ListView 元素如何知道视图应该可滚动以查看下面的元素。

这实际上取决于您通过将高度设置为匹配 childrenRect 来实现的目标,但在我的例子中,ListView 高度会根据子项而变化(大概是您的愿望)。有 100 个项目时,高度为 7970。模型中有 5 个项目时,结果为 350。您可以通过添加调试或 console.log()onHeightChanged 来检查这一点但是,由于这种缩放,假定 ListView 足够大以查看整个数据集,而不管 window 父容器大小如何。

您不需要缩放 ListView 的高度来匹配内容;这就是它的目的。它允许滚动因为内容太大而无法在其有限的高度内显示。

我能够通过简单地将语句更改为静态值来实现摆脱绑定循环并能够滚动,例如 800 的父高度:

Window {
  visible: true
  width: 800
  height: 800

      Rectangle {
          id: listContainer
          height: childrenRect.height
          width: parent.width
          color: "transparent"
          anchors {
              top: parent.top
              topMargin: 30
              left: parent.left
              leftMargin: 45
          }

          ListView {
              anchors.top: parent.top
              anchors.left: parent.left
              anchors.right: parent.right
              model: myModel
              height: 800//childrenRect.height

              header:
                  Text {
                    z: 2
                    height: 50
                    text: "HEADER"
                    color: "black"

              }
              delegate:  Component {
                  Item {
                      Text {
                          id:  userName;
                          text: name;
                          color: "black";
                          font.pixelSize: 50
                          anchors {
                              left: parent.left
                              leftMargin: 20
                          }
                      }

                      Rectangle {
                          height: 1
                          color: 'black'
                          width: listContainer.width
                          anchors {
                              left:  userName.left
                              top:  userName.top
                              topMargin: - 12
                              leftMargin: -15
                          }
                      }
                  }
              }
              spacing: 80
          }
      }

      ListModel {
          id: myModel
      }

      /* Fill the model with default values on startup */
      Component.onCompleted: {
          for (var i = 0; i < 100; i++) {
              myModel.append({
                  name: "Big Animal : " + i
              })
          }
      }
}

编辑:

我觉得您只是想为可缩放的 ListView 保护背景。将静态背景作为容器可以工作,但对于现代 unser 界面来说不是很好——任何反弹效果等都不会移动矩形。您可以通过将矩形锚定到 ListView 元素来实现此目的,但这是一种非常迂回的方式。相反,您可以只设置一个矩形来为 ListView 委托的每个元素设置样式。

delegate:  Component {
            Item {
                Rectangle{
                    width: listContainer.width
                    height: userName.height+13
                    //add 13 to adjust for margin set below
                    anchors {
                        left:  userName.left
                        top:  userName.top
                        topMargin: - 12
                        leftMargin: -15
                        //just copying from the other rectangle below
                    }
                    gradient: Gradient { 
                        //I am just using gradient here for a better understanding of spacing. You could use color.
                        GradientStop { position: 0.0; color: "aqua" }
                        GradientStop { position: 1.0; color: "green" }
                    }
                }
                Text {
                    id:  userName;
                    text: name;
                    color: "black";
                    font.pixelSize: 50
                    anchors {
                        left: parent.left
                        leftMargin: 20
                    }
                }
                Rectangle {
                    height: 1
                    color: 'black'
                    width: listContainer.width
                    anchors {
                        left:  userName.left
                        top:  userName.top
                        topMargin: - 12
                        leftMargin: -15
                    }
                }


            }
        }

这将确保 ListView 后面的矩形背景看起来像是随着项目滚动。实际上,我们将一个矩形分解为多个矩形,并为每个元素设置一个。例如,您还可以使用这种类型的样式来实现列表中的替代颜色。

您可能还把 Item 作为委托中的顶层,因为它没有给出任何隐式大小,ListView 使用它来计算滚动需求.您可以直接使用 Text 作为委托(您也不需要 Component)并将 line/rectangle 放在里面。如果这样做,您可以使用 ListViewcontentHeight 属性 来调整背景大小。

此外,我建议将 ListView 作为顶层并进行任何次要样式设置,我的意思是,将背景 Rectangle 放在里面。

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ListView {
        id: listView
        model: 3
        anchors.fill: parent

        Rectangle { //background
            color: "yellow"
            z: -1
            width: listView.width
            height: listView.contentHeight
        }

        delegate: Text {
            text: "name" + index
            color: "black";
            font.pixelSize: 50
            leftPadding: 20

            Rectangle {
                height: 1
                color: 'black'
                width: listView.width
                y: - 12
                x: -15
            }
        }
        spacing: 80

    }
}

顺便说一句,如果您打算将 ListView 放在某些 RowLayout 或其他东西中,您可能还想将 implicitHeight: contentHeight 放在 ListView.