Элемент ListView представляет один из наиболее часто используемых компонентов приложения и типов представлений, который позволяет создать список. Элементы в ListView могут располагаться горизонтально или вертикально. ListView наследуется от типа Flickable, который определяет базовый функционал для работы с элементами списков.
Рассмотрим простейший список ListView:
import QtQuick Window { width: 250 height: 200 visible: true title: "METANIT.COM" ListView { width: parent.width height: 150 model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } } delegate: Text { text: name + " (" + age + ")" } } }
Рассмотрим список по частям. Прежде всего, так как ListView наследуется от Flickable, а последний - от типа Item, то мы можем установить для ListView ширину и высоту.
Для определения используемых данных тип ListView определяет свойство model:
model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } }
Модель определяется типом ListModel из пакета "QtQml.Models". Это не единственный тип модели, который мы можем использовать. При необходимости мы можем создавать свои типы моделей. ListModel — это простой контейнер элементов ListElement, каждое из которых содержит роли данных. Содержимое ListModel может быть определено динамически или явно в QML.
Тип ListElement представляет отдельный элемент списка в ListModel. ListItem определяется, как и другие элементы в QML, только вместо свойств содержит
роли (согласно терминологии официальной документации). Например, в примере выше для каждого ListElement определяются две роли - "name" и
"age". Имена ролей должны начинаться со строчной буквы и быть общими для всех элементов в данной модели. Значения должны быть простыми константами: строки, логические значения
(true, false), числа или значения перечисления (например, AlignText.AlignHCenter
). Таким образом, в примере выше определяются три элемента, каждый из которых представляет данные человека.
Каждый элемент имеет две роли: name и age для хранения имени и возраста человека соответственно.
Другой компонент ListView - делегат определяет, как эти данные будут отображаться в списке, и устанавливается с помощью свойства delegate:
delegate: Text { text: name + " (" + age + ")" }
В примере выше делегат представляет элемент Text, у которого устанавливается свойство text
. Внутри делегата мы можем обратиться к ролям текущего элемента модели по их имени - name и age.
Таким образом, для каждого объекта модели будет создан элемент Text, который отобразит данные модели.
Вместо простого элемента Text можно было бы использовать более сложные композиции элементов. Например:
import QtQuick Window { width: 250 height: 200 visible: true title: "METANIT.COM" ListView { anchors.fill: parent model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } } delegate: Column { Text { text: "<b>Name:</b> " + name } Text { text: "<b>Age:</b> " + age } } } }
ListView может располагать свои элементы вертикально и горизонтально. Для этого управления ориентацией применяется свойство orientation, которое может принимать следующие значения:
Qt.Horizontal
: расположение по горизонтали
Qt.Vertical
: расположение по вертикали (значение по умолчанию)
По умолчанию список располагается по вертикали, поэтому теперь расположим по горизонтали:
import QtQuick Window { width: 250 height: 200 visible: true title: "METANIT.COM" ListView { anchors.fill: parent orientation: Qt.Horizontal spacing: 8 model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } } delegate: Column { Text { text: "<b>Name:</b> " + name } Text { text: "<b>Age:</b> " + age } } } }
Для управления направлением элементов ListView предоставляет еще пару свойств:
layoutDirection: управляет направлением по горизонтальни и может принимать значения Qt.LeftToRight
(расположение слева направо), либо
Qt.RightToLeft
(расположение справа налево).
verticalLayoutDirection: управляет направлением по горизонтальни и может принимать значения ListView.TopToBottom
(элементы располагаются сверху вниз), либо ListView.BottomToTop
(элементы располагаются снизу вверх).
За выделение элемента в списке ListView отвечает свойство highlight. Этому свойству можно назначить элемент, применяемый для выделения элемента списка. Например:
import QtQuick Window { width: 250 height: 200 visible: true title: "METANIT.COM" ListView { id: list anchors.fill: parent highlight: Rectangle { color: "lightsteelblue"; radius: 5 } focus: true // добавляем поддержку перемещения клавиатуров model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } } delegate: Column { padding: 5 width: list.width // растягиваем элемент по всей ширине контейнера Text { text: "<b>Name:</b> " + name } Text { text: "<b>Age:</b> " + age } } } }
Здесь в качестве элемента выделения применяется тип Rectangle, у которого устанавливается цвет - цвет выделения и радиус.
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
Для поддержки перемещения по списку клавиатурой свойству focus присваивается значение true
:
focus: true
При запуске приложения уже будет выделяться первый элемент:
Но тут мы можем столкнуться с проблемой - по умолчанию выделение мышью не доступно, и нам надо определить данный функционал самостоятельно. Для этого изменим код следующим образом:
import QtQuick Window { width: 250 height: 200 visible: true title: "METANIT.COM" ListView { id: list anchors.fill: parent highlight: Rectangle { color: "lightsteelblue"; radius: 5 } focus: true model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } } delegate: Item{ width: list.width height:45 Column { padding: 5 Text { text: "<b>Name:</b> " + name } Text { text: "<b>Age:</b> " + age } } MouseArea { anchors.fill: parent onClicked: list.currentIndex = index } } } }
В данном случае определение отдельного объекта списка вынесено в элемент Item, у которого установлены ширина и высота. И в этом элементе определен элемент MouseArea, который занимает всю область контейнера.
MouseArea { anchors.fill: parent onClicked: list.currentIndex = index }
MouseArea - это область нажатия мышью, при нажатии которой срабатывает обработчик onClicked
. В этом обработчике мы
устанавливаем индекс текущего элемента списка ListVie, передавая ему индекс нажатого элемента.
С помощью свойств header и footer можно задать соответственно заголовок и низ элемента ListView:
import QtQuick Window { width: 250 height: 200 visible: true title: "METANIT.COM" ListView { anchors.fill: parent id: list spacing: 8 header: Text { text: "Список пользователей" font.pixelSize: 15 } footer: Text { text: "Количество пользователей на 2023 год: " + list.count font.pixelSize: 11 font.italic: true } model: ListModel { ListElement { name: "Tom" age: 39 } ListElement { name: "Bob" age: 43 } ListElement { name: "Sam" age: 28 } } delegate: Column { Text { text: "<b>Name:</b> " + name } Text { text: "<b>Age:</b> " + age } } } }
В данном случае заголовок и футер представляют элементы Text. В футере выводится количество элементов, для этого идет обращение к свойство count текущего списка.
В общем случае в ListView делегат вызывается для каждого элемента ListItem и создает свой элемент, который отображается в ListView. Однако если элементов много, это может несколько
ухужшить производительность приложения при работе со списком. Но ListViw (начиная с версии Qt 5.15) позволяет повторное использовать ранее использованные элементы, когда они исчезают из поля зрения
при прокрутке. Этот подход повышает производительность в зависимости от сложности делегата.
По умолчанию повторное использование элементов отключено (по соображениям обратной совместимости), но его можно включить, установив для свойства reuseItems
значение true
.