Listvyer

En listvy kan hjälpa dig att enkelt visa många komponenter dynamiskt.

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:

  1. En modell, som innehåller data som du vill att din listvy ska visa
  2. En delegat, som definierar hur varje element i modellen kommer att visas
  3. 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ÄNDAHUR MAN KOMMER ÅT DENNÄR MAN ANVÄNDER DEN
Qt-modeller med mer än en rollmodel.index, model.someroleI de flesta fall
Qt-modeller med en rollmodel.index, model.somerole, model.modelDataI de flesta fall, för prototyper
Javascript-fältmodellmodel.index, model.modelDataFör prototyper
Heltalsmodellmodel.index, model.modelDataFö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:

Delegaten måste å andra sidan alltid ha sina dimensioner definierade. Vanligtvis är dess dimensioner inställda att endast använda vyns fulla bredd.

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:

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:

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.