ListView with Flow layout
-
My question is similar to this question, but it still not answered. I need a view for displaying items from C++ model with such features:
- This view has fixed width and height;
- Fixed width of each item;
- Dynamically adjusted item's height according to it's content;
- Items are laid out by columns;
- View has horizontal scroll.
It must look like this:
What I've tried?
- Manually calculate item's position:
delegate: Item { x: 200 y: 30 }
but it has no effect.
- Manually compose a view with
ScrollView
,Flow
,Repeater
. Using these types, the items can be laid out as needed, but this approach has a number of problems.ListView
doesn't load and render any invisible items, but Repeater are.ListView
provides attached property butRepeater
are not.
So, what is the best approach for compose such layout?
-
The layout you're describing — where items have a fixed width, dynamic height, are laid out vertically in columns, and the view scrolls horizontally — can be implemented in QML using a GridView with a few key settings:
Use flow: GridView.FlowTopToBottom to lay out items vertically in each column.
Set orientation: Qt.Horizontal to enable horizontal scrolling.
Define cellWidth to control the fixed width of each item.
Let each item calculate its own height dynamically by using implicitHeight based on its content (e.g., a Text element with wrapMode).
Use a GridView instead of Repeater + Flow so that off-screen items are not created unnecessarilyGridView { id: gridView width: 600 height: 400 cellWidth: 200 cellHeight: 100 flow: GridView.FlowTopToBottom model: myCppModel orientation: Qt.Horizontal boundsBehavior: Flickable.StopAtBounds clip: true delegate: Item { width: gridView.cellWidth height: contentItem.implicitHeight Rectangle { id: contentItem width: parent.width color: "lightgray" implicitHeight: text.implicitHeight + 20 Text { id: text text: model.display wrapMode: Text.WordWrap anchors.margins: 10 anchors.fill: parent } } } }
-
@SuhasKrishanamurthy, thanks for your help, but the
GridView
component has fixed cell's width and height. Items' height can grow up, but if it will be greater than cell's size, the collision occures. If I adjustedGridView
's cell size depending on size of largest item, there were too much free space between small items. The following code lays items out with collision:GridView { id: gridView width: 600 height: 400 cellWidth: 200 cellHeight: 100 flow: GridView.FlowTopToBottom model: ListModel { ListElement { value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit" } ListElement { value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } ListElement { value: "Lorem ipsum dolor sit amet" } } // orientation: Qt.Horizontal // GridView doesn't have this property boundsBehavior: Flickable.StopAtBounds clip: true delegate: Item { width: gridView.cellWidth height: contentItem.implicitHeight required property string value Rectangle { id: contentItem width: parent.width color: "lightgray" implicitHeight: tex.implicitHeight + 20 Text { id: tex text: value wrapMode: Text.WordWrap anchors.margins: 10 anchors.fill: parent } } } }
The desired behavior is lay items out onto the first column until it has free space and then turn into the next column (see picture in the question). Unfortunately,
GridView
lays item out strictly as a grid.This is partially implemented with
Flow
+Repeater
layout: