Visualizações de lista
List views podem ajudar você a exibir objetos de um modelo de forma atraente. Para usar uma visualização de lista, você precisa manter o controle de três coisas:
- O modelo, que contém os dados que você deseja que sua visualização de lista exiba
- O delegado, que define como cada elemento no modelo será exibido
- A própria visualização de lista, que exibirá informações do modelo de acordo com o delegado
Caso você queira mais esclarecimentos, a documentação do Qt tem uma página informativa sobre o assunto.
Noções básicas de modelos e visualizações
Uma visualização de lista tem duas propriedades essenciais às quais devemos prestar atenção:
- model, que aceita os dados ou o
iddo objeto que contém os dados - delegate, que aceita o componente que usaremos para exibir os dados no modelo
O modelo não é visível, pois contém apenas dados. Normalmente, o delegado será encapsulado em um componente para que seja reutilizável: ele serve como um modelo de como instanciar cada delegado.
Aqui está um exemplo que contém exatamente uma visualização de lista, um modelo e um delegado, usando um Kirigami.SubtitleDelegate:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "List of Plasma products"
width: 600
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
anchors.fill: parent
model: plasmaProductsModel
delegate: listDelegate
}
ListModel {
id: plasmaProductsModel
ListElement { product: "Plasma Desktop"; target: "desktop" }
ListElement { product: "Plasma Mobile"; target: "mobile" }
ListElement { product: "Plasma Bigscreen"; target: "TVs" }
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
text: `${model.product} is KDE software developed for ${model.target} stored at index ${model.index} of this list`
}
}
}
}E exatamente o mesmo exemplo, em linha:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "List of Plasma products (inline)"
width: 600
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
anchors.fill: parent
model: ListModel {
id: plasmaProductsModel
ListElement { product: "Plasma Desktop"; target: "desktop" }
ListElement { product: "Plasma Mobile"; target: "mobile" }
ListElement { product: "Plasma Bigscreen"; target: "TVs" }
}
delegate: Controls.ItemDelegate {
width: ListView.view.width
text: `${model.product} is KDE software developed for ${model.target} stored at index ${model.index} of this list`
}
}
}
}Compreendendo os modelos
O modelo contém os dados que serão usados para preencher a visualização de lista. Diferentes maneiras de usar modelos têm diferentes maneiras de acessar os dados:
| MANEIRA DE USAR | COMO ACESSAR | QUANDO USAR |
|---|---|---|
| Modelos Qt com mais de uma função | model.index, model.somerole | Na maioria dos casos |
| Modelos Qt com somente uma função | model.index, model.somerole, model.modelData | Na maioria dos casos, para prototipagem |
| Modelo de matriz JavaScript | model.index, model.modelData | Para prototipagem |
| Modelo inteiro | model.index, model.modelData | Para prototipagem |
Você pode ler sobre outras maneiras de usar modelos na documentação do Qt.
Na tabela acima, "modelos Qt" refere-se tanto a modelos específicos de C++, como QAbstractListModel, quanto a modelos específicos de QML, como ListModel. Esta página do tutorial se concentrará apenas em modelos específicos de QML. Mais adiante, forneceremos um tutorial para Conectar modelos C++ a QML usando QAbstractListModel.
A propriedade model.index é disponibilizada para cada modelo e contém o índice (a posição) de cada delegado. Ela pode ser abreviada para index por conveniência.
A propriedade model.somerole mencionada acima é apenas um espaço reservado, não é uma propriedade específica que vem do QML: somerole pode ser qualquer função definida pelo modelo. No primeiro exemplo de código desta página, mostrado acima da tabela, o modelo plasmaProductsModel possui as funções product e target, que podem ser acessadas com model.product e model.target, respectivamente.
Assim como model.index pode ser abreviado para index, cada propriedade model.somerole pode ser abreviada para apenas somerole (como product) por conveniência, mas é recomendado que sejam transformadas em propriedades obrigatórias:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "List of Plasma products (shortened with required properties)"
width: 600
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
anchors.fill: parent
model: plasmaProductsModel
delegate: listDelegate
}
ListModel {
id: plasmaProductsModel
ListElement { product: "Plasma Desktop"; target: "desktop" }
ListElement { product: "Plasma Mobile"; target: "mobile" }
ListElement { product: "Plasma Bigscreen"; target: "TVs" }
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
required property string product
required property string target
required property int index
text: `${product} is KDE software developed for ${target} stored at index ${index} of this list`
}
}
}
}Além disso, se o modelo contiver apenas uma função ou não tiver nenhuma função, seus dados também poderão ser acessados com a propriedade model.modelData, que também pode ser abreviada para modelData (e, como tal, também precisaria ser uma propriedade obrigatória):
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "List of KDE software"
width: 400
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
anchors.fill: parent
model: kdeSoftwareModel
delegate: listDelegate
}
ListModel {
id: kdeSoftwareModel
ListElement { software: "Dolphin" }
ListElement { software: "Discover" }
ListElement { software: "KHelpCenter" }
ListElement { software: "KCalc" }
ListElement { software: "Ark" }
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
required property string modelData
text: modelData // Isto corresponde à model.software
}
}
}
}Para comparação, veja como o código acima ficaria com uma matriz JavaScript, sem função:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "List of KDE software (as JS array)"
width: 400
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
anchors.fill: parent
model: ["Dolphin", "Discover", "KHelpCenter", "KCalc", "Ark"]
delegate: listDelegate
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
required property string modelData
text: modelData
}
}
}
}Usar um número inteiro para o modelo pode ser útil para casos muito específicos, como prototipagem e testes:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "Simple list of indexes"
width: 400
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
anchors.fill: parent
model: 30
delegate: listDelegate
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
required property string modelData
text: `This delegate's index is: ${modelData}`
}
}
}
}Compreendendo visões e delegados
Vamos voltar ao exemplo original:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
Kirigami.ApplicationWindow {
title: "List of Plasma products"
width: 600
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
// anchors.fill: parent
model: plasmaProductsModel
delegate: listDelegate
}
ListModel {
id: plasmaProductsModel
ListElement { product: "Plasma Desktop"; target: "desktop" }
ListElement { product: "Plasma Mobile"; target: "mobile" }
ListElement { product: "Plasma Bigscreen"; target: "TVs" }
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
text: `${model.product} is KDE software developed for ${model.target} stored at index ${model.index} of this list`
}
}
}
}Ao contrário do modelo (que contém apenas dados) e de um delegado Componente (que só aparece quando instanciado), a visão é um componente visual imediatamente instanciado e, portanto, precisa ter suas dimensões definidas ou usar âncoras ou Layouts.
Como as visões são comumente listas de conteúdo que o usuário deseja percorrer, quando são adicionadas a uma Kirigami.ScrollablePage, as visões se tornam o conteúdo principal com pouco espaço ao redor delas, e não há necessidade de fazê-las preencher a página. Quando a visão é adicionada a uma Kirigami.Page simples, será necessário definir suas dimensões corretamente antes de ser exibida. Em outras palavras: na página rolável acima, anchors.fill: parent não é necessário; se uma página simples fosse usada, seria necessário.
Existem diversas APIs de visão que podem ser utilizadas, algumas do Qt e outras do Kirigami. Aqui estão as mais comumente utilizadas:
- ListView do Qt
- GridView do Qt
- TableView do Qt
- TreeView do Qt
- CardsListView do Kirigami
- ColumnView do Kirigami
O delegado, por outro lado, sempre precisa ter suas dimensões definidas. Geralmente, suas dimensões são definidas para usar apenas a largura total da visão.
Erros comuns
O acima significa que os delegados não devem ter âncoras inferiores, já que o delegado não precisa ter a mesma altura que a visão. Em outras palavras, você provavelmente nunca vai querer usar anchors.fill: parent.
Além disso, embora seja possível definir suas dimensões usando o pai e as âncoras, que geralmente são o contentItem da visão, assim:
Controls.ItemDelegate {
anchors.left: parent.left
anchors.right: parent.right
text: // ...
}Não há garantia de que o pai do delegado seja uma visão e, portanto, isso deve ser evitado. Em vez disso, use a propriedade anexada ListView.view para apontar para a visão pai do delegado:
Controls.ItemDelegate {
width: ListView.view.width
text: // ...
}O uso mais comum de um delegado é dentro de um Componente, que não instancia o delegado imediatamente. Quando uma visão é construída, o delegado é então usado como um modelo para criar cada item na visão.
Embora você possa criar seus próprios componentes personalizados para serem usados como delegados sem APIs Qt específicas para delegados (por exemplo, um Layout contendo alguns Itens), o QtQuick Controls fornece APIs de delegados que são mais simples de usar:
- ItemDelegate (delegados com somente texto)
- CheckDelegate (delegados com uma caixa de seleção)
- RadioDelegate (delegados com um botão de opção)
- SwitchDelegate (delegados com um interruptor)
- SwipeDelegate (delegados que podem ser passados)
Você deve preferir usar os delegados Qt upstream sempre que possível.
Além desses delegados do Qt, o Kirigami fornece seus próprios equivalentes, com a funcionalidade adicional de legendas e ícones:
- TitleSubtitle
- IconTitleSubtitle
- SubtitleDelegate
- CheckSubtitleDelegate
- RadioSubtitleDelegate
- SwitchSubtitleDelegate
A API que termina com "Delegate" pode ser definida como um delegado direto da visão, assim como os exemplos anteriores que usaram Controls.ItemDelegate:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
import org.kde.kirigami.delegates as KD
Kirigami.ApplicationWindow {
title: "List of Plasma products"
width: 600
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
model: plasmaProductsModel
delegate: listDelegate
}
ListModel {
id: plasmaProductsModel
ListElement { product: "Plasma Desktop"; target: "desktop" }
ListElement { product: "Plasma Mobile"; target: "mobile" }
ListElement { product: "Plasma Bigscreen"; target: "TVs" }
}
Component {
id: listDelegate
KD.CheckSubtitleDelegate {
width: ListView.view.width
text: `${model.product} is KDE software developed for ${model.target}.`
subtitle: `This delegate is stored at index ${model.index} of this list`
icon.name: "kde"
}
}
}
}Espera-se que TitleSubtitle e IconTitleSubtitle sejam usados para substituir o contentItem de um delegado Qt, por exemplo:
import QtQuick
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami
import org.kde.kirigami.delegates as KD
Kirigami.ApplicationWindow {
title: "List of Plasma products"
width: 600
height: 400
pageStack.initialPage: Kirigami.ScrollablePage {
ListView {
// anchors.fill: parent
model: plasmaProductsModel
delegate: listDelegate
}
ListModel {
id: plasmaProductsModel
ListElement { product: "Plasma Desktop"; target: "desktop" }
ListElement { product: "Plasma Mobile"; target: "mobile" }
ListElement { product: "Plasma Bigscreen"; target: "TVs" }
}
Component {
id: listDelegate
Controls.ItemDelegate {
width: ListView.view.width
text: `${model.product} is KDE software developed for ${model.target}.`
contentItem: KD.IconTitleSubtitle {
title: parent.text
subtitle: `This delegate is stored at index ${model.index} of this list`
icon.name: "kde"
}
}
}
}
}Um exemplo prático do uso de delegados Kirigami pode ser visto no arquivo ListItemTest no Repositório Kirigami.