Viste a elenco
Visualizzazioni elenco può aiutarti a visualizzare gli oggetti da un modello in modo attraente. Per utilizzare una visualizzazione elenco, devi tenere traccia di tre cose:
- Il modello, che contiene i dati che vuoi visualizzare nella vista a elenco
- Il delegato, che definisce come verrà visualizzato ciascun elemento del modello
- La vista a elenco stessa, che visualizza le informazioni dal modello secondo il delegato
Se desideri ulteriori chiarimenti, la documentazione Qt contiene una pagina informativa sull'argomento.
Elementi essenziali di modelli e viste
Una vista a elenco ha due proprietà essenziali a cui devi prestare attenzione:
- model, che accetta i dati o l'"id" dell'oggetto che contiene i dati
- delegate, che accetta il componente che utilizzeremo per visualizzare i dati nel modello
Il modello non è visibile poiché contiene solo dati. In genere il delegato verrà racchiuso in un componente in modo che sia riutilizzabile: funge da modello su come creare un'istanza di ciascun delegato.
Ecco un esempio che contiene esattamente una visualizzazione elenco, un modello e un delegato, utilizzando un 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 lo stesso identico esempio, in linea:
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`
}
}
}
}Capire i modelli
Il modello contiene i dati che verranno utilizzati per popolare la visualizzazione elenco. Modi diversi di utilizzare i modelli comportano modi diversi di accedere ai dati:
| MODO D'USO | COME ACCEDERE | QUANDO USARE |
|---|---|---|
| Modelli Qt con più di un ruolo | model.index, model.somerole | Nella maggior parte dei casi |
| Modelli Qt con un ruolo | model.index, model.somerole, model.modelData | Nella maggior parte dei casi, per la prototipazione |
| Modello di array JavaScript | model.index, model.modelData | Per la prototipazione |
| Modello intero | model.index, model.modelData | Per la prototipazione |
Puoi leggere informazioni su altri modi per utilizzare i modelli nella documentazione Qt.
Nella tabella sopra, "modelli Qt" si riferisce sia a modelli specifici di C++ come QAbstractListModel sia a modelli specifici di QML come ListModel. Questa pagina del tutorial si concentrerà solo sui modelli specifici di QML. Più avanti verrà fornito un tutorial per Connettere modelli C++ a QML utilizzando QAbstractListModel.
La proprietà model.index è resa disponibile a ogni modello e contiene l'indice (la posizione) di ciascun delegato. Può essere abbreviato in "indice" per comodità.
La proprietà model.somerole menzionata sopra è solo un segnaposto, non è una proprietà specifica che proviene da QML: somerole può essere qualsiasi ruolo definito dal modello. Nel primo esempio di codice di questa pagina mostrato sopra la tabella, il modello plasmaProductsModel ha i ruoli "product" e "target", a cui è possibile accedere rispettivamente con "model.product" e "model.target".
Proprio come "model.index" può essere abbreviato in "index", ogni proprietà "model.somerole" può essere abbreviata semplicemente in "somerole" (come "product") per comodità, ma si consiglia di trasformarle in proprietà richieste:
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`
}
}
}
}Inoltre, se il modello contiene un solo ruolo o non ne ha alcuno, è possibile accedere ai suoi dati anche con la proprietà "model.modelData", che può anche essere abbreviata in "modelData" (e come tale dovrebbe anche essere una proprietà obbligatoria):
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 // Questo corrisponde a model.software
}
}
}
}Per confronto, ecco come apparirebbe il codice precedente con un array JavaScript, senza ruolo:
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
}
}
}
}L'utilizzo di un numero intero per il modello può essere utile per casi molto specifici, ovvero prototipazione e test:
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}`
}
}
}
}Capire le viste e i delegati
Torniamo all'esempio iniziale:
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: genitore
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`
}
}
}
}A differenza del modello (che contiene semplicemente dati) e di un delegato Component (che appare solo quando istanziato), la vista è un componente visivo istanziato immediatamente e quindi deve avere le sue dimensioni impostate o utilizzare ancore o layout.
Poiché le visualizzazioni sono comunemente elenchi di contenuti che l'utente vorrebbe scorrere, quando vengono aggiunte a una Kirigami.ScrollablePage, le visualizzazioni diventano il contenuto principale con poca imbottitura attorno ad esse e non è necessario farle riempire la pagina. Quando la vista viene aggiunta a una semplice Kirigami.Page, sarà necessario impostarne correttamente le dimensioni prima di essere visualizzata. In altre parole: nella pagina scorrevole sopra, anchors.fill: parent non è richiesto; se fosse utilizzata una pagina semplice, sarebbe necessaria.
È possibile utilizzare più API di visualizzazione, alcune da Qt e altre da Kirigami. Ecco quelli più comunemente usati:
- ListView di Qt
- [GridView] di Qt(docs:qtquick;QtQuick.GridView)
- [TableView] di Qt(docs:qtquick;QtQuick.TableView)
- [TreeView] di Qt(docs:qtquick;QtQuick.TreeView)
- [CardsListView] di Kirigami(docs:kirigami;org.kde.kirigami.CardsListView)
- [ColumnView] di Kirigami(docs:kirigami;org.kde.kirigami.layouts.ColumnView)
Il delegato invece deve sempre avere le sue dimensioni impostate. Generalmente le sue dimensioni sono impostate per utilizzare solo l'intera larghezza della vista.
Errori comuni
Quanto sopra significa che i delegati non dovrebbero avere ancoraggi inferiori, poiché non è necessario che il delegato abbia la stessa altezza della vista. In altre parole, probabilmente non vorrai mai utilizzare anchors.fill: parent.
Inoltre, sebbene sia possibile impostarne le dimensioni utilizzando il genitore e gli ancoraggi, che di solito è il contentItem della vista, in questo modo:
Controls.ItemDelegate {
anchors.left: parent.left
anchors.right: parent.right
text: // ...
}Non è garantito che l'elemento padre del delegato sia una visualizzazione, pertanto è opportuno evitarlo. Utilizza invece la proprietà associata ListView.view per puntare alla vista principale del delegato:
Controls.ItemDelegate {
width: ListView.view.width
text: // ...
}L'uso più comune di un delegato è all'interno di un Component, che non istanzia immediatamente il delegato. Quando viene costruita una vista, il delegato viene quindi utilizzato come modello per creare ogni elemento nella vista.
Sebbene sia possibile creare componenti personalizzati da utilizzare come delegati senza API Qt specifiche del delegato (ad esempio, un layout contenente alcuni elementi), QtQuick Controls fornisce API delegate più semplici da utilizzare:
- ItemDelegate (delega solo con testo)
- CheckDelegate (delegati con una casella di controllo)
- RadioDelegate (delegati con una radio)
- SwitchDelegate (delegati con un interruttore)
- SwipeDelegate (delegati che possono essere spostati)
Dovresti preferire l'utilizzo dei delegati Qt upstream ove possibile.
Oltre a questi delegati Qt, Kirigami fornisce i propri equivalenti, con la funzionalità aggiuntiva di sottotitoli e icone:
- TitleSubtitle
- IconTitleSubtitle
- SubtitleDelegate
- CheckSubtitleDelegate
- RadioSubtitleDelegate
- SwitchSubtitleDelegate
L'API che termina con "Delegate" può essere impostata come delegato diretto della vista, proprio come gli esempi precedenti che utilizzavano 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"
}
}
}
}È previsto che sia TitleSubtitle che IconTitleSubtitle vengano utilizzati per sovrascrivere il contentItem di un delegato Qt, ad esempio:
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: genitore
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"
}
}
}
}
}Un esempio pratico di utilizzo dei delegati Kirigami può essere visto nel file ListItemTest nel repository Kirigami.