QML ListView: Обнаружен цикл привязки для свойства «высота»

#qt #listview #qml #qtquick2

Вопрос:

У меня есть представление списка QML, и я пытаюсь динамически добавлять в него элементы. Я хочу, чтобы прямоугольник фона также динамически масштабировался по мере добавления/удаления элементов из представления списка. Прямо сейчас я получаю петлю привязки, и я понимаю, что это такое, но не могу понять, откуда она берется. Я немного поиграл, изменив код, и мне удалось один раз избавиться от цикла привязки, но затем представление списка не удалось прокрутить. У кого — нибудь есть какие-нибудь идеи?

 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, цикл привязки можно удалить, установив статическую высоту представления списка, но я не хочу, чтобы это было так. Я использую прямоугольник в качестве фона для представления списка и хочу, чтобы он масштабировался в соответствии с представлением списка. Например, если я добавлю только два элемента, я хочу, чтобы прямоугольник также масштабировался для этих двух элементов, а не покрывал весь экран. Это вызывает проблему:

 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 в другой компонент и закрепить listview под ним, и это сработало. Единственная проблема заключалась в том, что его нельзя было прокрутить с помощью представления списка. В худшем случае я мог бы сделать анимацию прокрутки для этого, но это кажется неэффективным решением, и я хотел бы знать, почему это не работает.

Ответ №1:

Вы, вероятно, также кусаете себя за Item то, что являетесь делегатом верхнего уровня, поскольку это не дает никакого неявного размера, который ListView используется для расчета потребностей прокрутки. Вы можете просто использовать Text непосредственно в качестве делегата (вам это тоже не нужно Component ) и поместить линию/прямоугольник внутрь. При этом вы можете использовать contentHeight свойство ListView для изменения размера фона.

Кроме того, я бы предложил 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

    }
}
 

Кстати, если вы собираетесь поместить представление списка в какое RowLayout -то или что-то, вы, вероятно, также захотите implicitHeight: contentHeight в ListView .

Ответ №2:

Цикл привязки происходит из оператора ListView height: childrenRect.height . Похоже, что ListView должен иметь фиксированную высоту или, по крайней мере, не зависеть от childrenRect. Скорее всего, элемент ListView знает, что представление должно быть прокручиваемым для просмотра элементов ниже.

Это действительно зависит от того , чего вы пытаетесь достичь, установив соответствующую высоту childrenRect , но в моем случае высота ListView меняется в зависимости от детей (предположительно, по вашему желанию). При 100 предметах высота получилась 7970. С 5 элементами в модели результат составил 350. Вы можете проверить это, добавив отладку или console.log() с onHeightChanged помощью, однако в результате такого масштабирования предполагается, что представление списка достаточно большое для просмотра всего набора данных независимо от окно размер родительского контейнера.

Вам не нужно масштабировать высоту списка, чтобы соответствовать содержимому; для этого он и создан. Он позволяет прокручивать, потому что содержимое слишком велико, чтобы его можно было отобразить в пределах ограниченной высоты.

Я смог избавиться от цикла привязки и иметь возможность прокручивать, просто изменив оператор на статическое значение, которое является родительской высотой 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
              })
          }
      }
}
 

Редактировать:

Я чувствую, что вы пытаетесь просто обеспечить фон для масштабируемого представления списка. Наличие статического фона в качестве контейнера работает, но не очень хорошо для современных интерфейсов 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
                    }
                }


            }
        }
 

Это гарантирует, что фон прямоугольника за представлением списка будет выглядеть так, как будто он прокручивается с элементами. На самом деле мы разбили один прямоугольник на несколько и просто задали каждому элементу по одному. Вы также можете использовать этот тип укладки, например, для достижения альтернативных цветов в вашем списке.

Комментарии:

1. Да, я тоже попробовал это, и это сработало, за исключением того, что я использую прямоугольник в качестве фона для списка и хочу, чтобы он масштабировался вместе со списком. Есть идеи, как я мог бы сделать это, не сталкиваясь с проблемой цикла привязки?

2. Прямоугольник останется за списком без каких-либо проблем. С какой проблемой вы здесь столкнулись? Я изменил цвет прямой кишки на фиолетовый, и она остается статичной, как и ожидалось.

3. Например, допустим, я добавляю только два элемента (для цикла в конце выполняется только дважды), мне нужен цветной фон только для этих элементов, но сейчас фон покрывает весь экран, потому что он имеет статическую высоту. Я отредактировал вопрос с примером, который покажет проблему.

4. В этом случае вам нужно будет отрегулировать высоту прямой кишки, а не списка. Что-то вроде height: list.childrenRect.height и. ListView {id: list} Отрегулируйте с помощью поля и интервала (возможно, вам потребуется добавить к этому статическое значение). Однако прямоугольник будет статичным. Позиция не изменится в зависимости от эффекта отскока при чрезмерной прокрутке. Над этим нужно еще поработать. Почему бы вам вместо этого не изменить стиль делегирования элемента списка?

5. О, это работает, я думаю. Я чувствую, что пробовал это, но мог что-то упустить. Спасибо за вашу помощь!