Listvyer
Listvyer kan hjälpa dig att visa objekt från en modell på ett attraktivt sätt. För att använda en listvy måste du hålla redan på tre saker:
- En modell, som innehåller data som du vill att din listvy ska visa
- En delegat, som definierar hur varje element i modellen kommer att visas
- Själva listvyn, som visar information från modellen enligt delegaten
Om du vill ha ytterligare klargöranden, har Qt-dokumentationen en informativ sida om ämnet.
Grunderna i modeller och vyer
En listvy har två grundläggande egenskaper som vi måste ta hänsyn till:
- model, som accepterar objektets
id
som innehåller data - delegate, som accepterar komponenten vi ska använda för att visa data i modellen
Modellen är inte synlig, eftersom den endast innehåller data. Vanligtvis omges delegaten av en komponent så att den är återanvändbar: den fungerar som en ritning för hur man instansierar varje delegat.
Här är ett exempel som innehåller exakt en listvy, en modell och en delegat, med en 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`
}
}
}
}
Och exakt samma exempel på plats:
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`
}
}
}
}
Förstå modeller
Modellen innehåller data som användas för att fylla listvyn. Olika sätt att använda modeller har olika sätt att komma åt data:
SÄTT ATT ANVÄNDA | HUR MAN KOMMER ÅT DEN | NÄR MAN ANVÄNDER DEN |
---|---|---|
Qt-modeller med mer än en roll | model.index, model.somerole | I de flesta fall |
Qt-modeller med en roll | model.index, model.somerole, model.modelData | I de flesta fall, för prototyper |
Javascript-fältmodell | model.index, model.modelData | För prototyper |
Heltalsmodell | model.index, model.modelData | För prototyper |
Du kan läsa om andra sätt att använda modeller i Qt-dokumentationen.
I tabellen ovan hänvisar "Qt-modeller" till både C++ specifika modeller som QAbstractListModel och QML-specifika modeller som ListModel. Den här sidan i handledningen fokuserar enbart på QML-specifika modeller. Längre fram tillhandahåller vi en handledning för att Ansluta C++ modeller till QML med QAbstractListModel.
Egenskapen model.index
görs tillgänglig för varje modell och innehåller indexet (positionen) för varje delegat. Det kan för enkelhets skull förkortas till "index".
Egenskapen model.somerole
som nämns ovan är bara en platsmarkör, den är inte en specifik egenskap som kommer från QML: somerole
kan vara vilken roll som helst som definieras av modellen. I det första kodexemplet på den här sidan som visas ovanför tabellen har "plasmaProductsModel" modellen "product" och rollerna "target", som kan nås med "model.product" respektive "model.target".
Precis som "model.index" kan förkortas till "index", kan varje egenskap "model.somerole" kortas till bara "somerole" (som "product") för enkelhetens skull, men det rekommenderas att de omvandlas till nödvändiga egenskaper:
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`
}
}
}
}
Dessutom, om modellen bara innehåller en roll eller inte har någon roll alls, kan dess data också nås med egenskapen "model.modelData", som också kan förkortas till "modelData" (och som sådan också skulle behöva vara en nödvändig egenskap):
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 // Det motsvarar model.software
}
}
}
}
För jämförelse, så här skulle ovanstående kod se ut med ett Javascript-fält, utan roll:
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
}
}
}
}
Att använda ett heltal för modellen kan vara användbart för mycket specifika fall, nämligen prototyper och tester:
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}`
}
}
}
}
Förstå vyer och delegater
Låt oss gå tillbaka till det ursprungliga exemplet:
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`
}
}
}
}
Till skillnad från modellen (som bara innehåller data) och en delegat Component (som bara visas när den instansieras), är vyn en visuell komponent som omedelbart instansieras och därför måste den antingen ha sina dimensioner inställda eller använd förankringar eller layouter.
Eftersom vyer vanligtvis är listor med innehåll som användaren skulle vilja bläddra igenom, när de läggs till på en Kirigami.ScrollablePage, blir vyerna huvudinnehållet med lite utfyllnad runt dem, och det finns inget behov för att få dem att fylla sidan. När vyn läggs till på en enkel Kirigami.Page, måste den ställa in sina dimensioner ordentligt innan den dyker upp. Med andra ord: på den rullningsbara sidan ovan krävs inte anchors.fill: parent
, men om en enkel sida användes skulle den krävas.
Det finns flera programmeringsgränssnitt för vyer som kan användas, några från Qt och några från Kirigami. Här är de mest använda:
- Qt:s ListView
- Qt:s GridView
- Qt:s TableView
- Qt:s TreeView
- Kirigamis CardsListView
- Kirigamis ColumnView
Delegaten måste å andra sidan alltid ha sina dimensioner definierade. Vanligtvis är dess dimensioner inställda att endast använda vyns fulla bredd.
Vanliga misstag
Ovanstående betyder att delegater inte ska ha förankringar längst ner, eftersom delegaten inte behöver ha samma höjd som vyn. Med andra ord, man vill förmodligen aldrig använda anchors.fill: parent
.
Dessutom, även om det är möjligt att ställa in dimensionerna med hjälp av överliggande objekt och förankring, som vanligtvis är vyns contentItem, så här:
Controls.ItemDelegate {
anchors.left: parent.left
anchors.right: parent.right
text: // ...
}
Det är inte garanterat att delegatens överliggande objekt är en vy, och det bör därför undvikas. Använd istället den bifogade egenskapen ListView.view för att peka på delegatens överliggande vy:
Controls.ItemDelegate {
width: ListView.view.width
text: // ...
}
Den vanligaste användningen av en delegat är inom en Component, som inte instansierar delegaten omedelbart. När en vy är konstruerad, används sedan delegaten som en ritning för att skapa varje objekt i vyn.
Även om man kan skapa sina egna komponenter för att användas som delegater utan delegatspecifika Qt API:er (till exempel en layout som innehåller några objekt), tillhandahåller QtQuick Controls programmeringsgränssnitt för delegater som är enklare att använda:
- ItemDelegate (delegater med bara text)
- CheckDelegate (delegater med en kryssruta)
- RadioDelegate (delegater med en alternativknapp)
- SwitchDelegate (delegater med en omkopplare)
- SwipeDelegate (delegater som kan svepas)
Man bör föredra att använda uppströms Qt-delegater där det är möjligt.
Utöver dessa Qt-delegater tillhandahåller Kirigami sina egna motsvarigheter, med den extra funktionaliteten av undertexter och ikoner:
- TitleSubtitle
- IconTitleSubtitle
- SubtitleDelegate
- CheckSubtitleDelegate
- RadioSubtitleDelegate
- SwitchSubtitleDelegate
Programmeringsgränssnittet som slutar med "Delegate" kan anges som en direkt delegat i vyn, precis som de tidigare exemplen som använde 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"
}
}
}
}
Både TitleSubtitle och IconTitleSubtitle förväntas användas för att överskrida en Qt-delegats contentItem, till exempel:
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"
}
}
}
}
}
Ett praktiskt exempel på att använda Kirigami-delegater kan ses i filen ListItemTest i Kirigami-arkivet.