QML ListView:检测到属性“高度”的绑定循环

Posted

技术标签:

【中文标题】QML ListView:检测到属性“高度”的绑定循环【英文标题】:QML ListView: Binding loop detected for property "height" 【发布时间】:2021-09-20 20:38:19 【问题描述】:

我有一个 QML 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: "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
              )
          
      

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

【问题讨论】:

【参考方案1】:

您可能还会将Item 作为代理中的***,因为它没有给出任何隐式大小,ListView 使用它来计算滚动需求。您可以简单地将Text 直接用作代表(您也不需要Component)并将线条/矩形放入其中。如果这样做,您可以使用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 中。

【讨论】:

【参考方案2】:

绑定循环源自 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 后面的矩形背景看起来像是随着项目滚动。实际上,我们已经将一个矩形分成多个,并且只为每个元素设置一个。例如,您还可以使用这种样式来实现列表中的替代颜色。

【讨论】:

是的,我也试过了,而且效果很好,只是我使用矩形作为列表的背景,我希望它与列表一起缩放。知道如何在不遇到此绑定循环问题的情况下完成此操作吗? 矩形将留在 ListView 后面,没有任何问题。你在这里面临什么问题?我将矩形的颜色更改为紫色,它按预期保持静态。 例如,假设我只添加了两个元素(最后的 for 循环只运行两次),我只想要这些元素的颜色背景,但现在背景覆盖了整个屏幕,因为它有静态高度。我用一个可以显示问题的示例编辑了问题。 在这种情况下,您需要调整矩形的高度,而不是 ListView。类似于height: list.childrenRect.heightListView id: list。调整边距和间距(您可能需要为此添加静态值)。然而,矩形将是静态的。位置不会根据过度滚动的反弹效果而改变。这需要更多的工作。为什么不改为更改列表元素委托样式? 哦,我认为这行得通。我觉得我试过了,但我可能错过了一些东西。感谢您的帮助!

以上是关于QML ListView:检测到属性“高度”的绑定循环的主要内容,如果未能解决你的问题,请参考以下文章

检测到属性宽度的 QML 绑定循环(TextMetrics 行为怪异)

QML:在没有双重分配的情况下检测到绑定循环

从高度动态的 C++ 数据模型更新 QML:计时器与属性绑定

Qml ListView动态高度项过渡

标签上的绑定循环警告

提问qml中的listview中的item怎么自适应高度