Disposicions (Layout), vistes de llista (ListView) i targetes (Card)

Descobrint les formes diferents de col·locar les coses en una pàgina

Dissenyar el contingut

Ara que entenem com funcionen les pàgines, és hora d'afegir-hi coses. Repassarem una sèrie de components i elements de disposició importants que seran útils en dissenyar la nostra aplicació.

Al final d'aquesta secció tindreu una aplicació d'aspecte polit.

Vistes de llista (ListView)

Si heu emprat alguna vegada el Discover, el NeoChat o l'Arranjament del sistema del Plasma, us haureu trobat amb una ListView. En poques paraules, les vistes de llista permeten mostrar les dades en una llista.

pageStack.initialPage: Kirigami.ScrollablePage {
    // ...
    Kirigami.CardsListView {
       id: cardsView
        model: kountdownModel
        delegate: kountdownDelegate
    }
}

Això sembla críptic, però no us preocupeu. Comencem des del principi.

Ara afegirem aquest component dins del nostre Kirigami.ScrollablePage de la darrera guia d'aprenentatge.

Estem emprant la Kirigami.CardsListView, que és una ListView que ens permet mostrar targetes en una llista amb facilitat. No obstant això, les ListViews estan fetes per a mostrar les dades preses a partir d'un model, per a emplenar-se automàticament a partir d'un conjunt de dades a les quals apuntem. Aquí és on entra la propietat model: en aquest exemple, apunta a kountdownModel.

Model

Kirigami.ApplicationWindow {
    // ...
    ListModel {
        id: kountdownModel
        // Cada ListElement és un element en la llista que conté informació
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }
    // ...
}

Afegirem el kountdownModel dins de la Kirigami.ApplicationWindow de la darrera guia d'aprenentatge.

Un model defineix la manera en què s'estructura una entrada de dades. Per ara, el nostre kountdownModel només consistirà d'un element. Si mirem el nostre ListElement anterior, veurem com s'estructuren els elements del kountdownModel: conté un nom, una descripció i una data. Això no és immutable, i podeu tenir diversos tipus de dades en el vostre model. Els dos primers són cadenes i el tercer és un número que utilitzem com a marcador de posició.

Els models també són útils perquè es poden modificar mitjançant l'ús de diversos mètodes. Alguns importants són:

  • ListModel.append(yourobject: jsobject) afegeix un objecte de JavaScript (JSObject) yourobject al ListModel, i el col·loca després de l'últim element del model. Perquè això succeeixi correctament, cal proporcionar un objecte JSObject amb les propietats correctes i els tipus de dades corresponents.
  • ListModel.get(index: int) retorna el JSObject en la ubicació de l'índex que heu proporcionat.
  • ListModel.remove(index: int, count: int) elimina el JSObject en la ubicació proporcionada index, i tantes com vulgueu després d'aquesta ubicació de l'índex com indiqueu a count (1 només inclou el JSObject a l'índex proporcionat).
  • ListModel.set(index: int, yourobject: jsobject) canvia l'element en la ubicació proporcionada de index amb els valors proporcionats a yourobject. Les mateixes regles que amb .append().

Delegats

Mentre que kountdownModel conté les dades que es mostraran, el nostre kountdownDelegate gestionarà com es mostraran les dades a la ListView. Per això farem servir una Kirigami.CardsListView dissenyada per a mostrar delegats del tipus targetes i aquests delegats es representaran visualment per mitjà d'una Kirigami.AbstractCard.

Els delegats reben automàticament les propietats dels ListElements que hem especificat en el nostre model. Per tant, només podem fer referència a les seves propietats name, description i date com si fossin variables convencionals dins del nostre delegat.

Construir la targeta de delegat

El Component que representarà el nostre delegat es pot afegir dins del nostre Kirigami.ApplicationWindow. Després comprovarem que fa cada part del nostre component delegat.

Kirigami.ApplicationWindow {
    // ...
    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                // implicitWidth/Height defineix l'amplada/alçada natural d'un element si no se
                // n'especifica cap. La configuració següent defineix la mida preferida d'un
                // component en funció del seu contingut.
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        Layout.fillHeight: true
                        level: 1
                        text: date
                    }

                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        Controls.Label {
                            Layout.fillWidth: true
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        Layout.columnSpan: 2
                        text: i18n("Edit")
                        // onClicked: s'ha de fer... aviat!
                    }
                }
            }
        }
    }
    // ...
}

implicitWidth i implicitHeight

A la primera part donarem un cop d'ull a com gestionar l'amplada i l'alçada del nostre component:

Kirigami.AbstractCard {
    contentItem: Item {
        implicitWidth: delegateLayout.implicitWidth
        implicitHeight: delegateLayout.implicitHeight
        GridLayout {
            id: delegateLayout
            // ...
        }
    }
}

Mirant la nostra Kirigami.AbstractCard, les primeres propietats que establim són implicitWidth i implicitHeight. Les hem establert a la delegateLayout.implicitWidth i delegateLayout.implicitHeight, és a dir, les implicitWidth i implicitHeight de l'element GridLayout.

Les amplades i alçades implícites són propietats disponibles en qualsevol Item que funcionen com a consells i s'estableixen com a valor predeterminat, o com a reserva, si no hi ha una amplada o alçada explícita establerta per a aquests components. Aquests valors són predeterminats a 0x0, per tant, és molt important que els definiu en components Item en brut tal com s'ha fet anteriorment.

Aquí haurem establert la implicitWidth i implicitHeight de la nostra Kirigami.AbstractCard a la de la GridLayout següent per a assegurar-nos que no desbordi la targeta. D'aquesta manera, la targeta pren tant espai com sigui necessari per al seu contingut.

Disposicions

La GridLayout es troba dins del component Item que hem proporcionat per a la propietat contentItem. Aquest és l'Item que conté el que es mostrarà a la targeta.

També haurem de triar una disposició «layout» per als nostres components, de manera que no s'apilin un damunt de l'altre. Hi ha tres tipus principals entre les quals podem triar:

  • ColumnLayout distribueix els components verticalment, en una sola columna
  • RowLayout distribueix els components horitzontalment, en una sola fila
  • GridLayout distribueix els components en una quadrícula amb una composició de la vostra elecció

Amb ColumnLayout i RowLayout, tot el que hem de fer és escriure els nostres components dins del component «Layout». Com podeu veure, hem optat per una GridLayout, la qual implica una mica més de treball manual.

GridLayout {
    id: delegateLayout
    anchors {
        left: parent.left
        top: parent.top
        right: parent.right
    }
    rowSpacing: Kirigami.Units.largeSpacing
    columnSpacing: Kirigami.Units.largeSpacing
    columns: root.wideScreen ? 4 : 2
    // ...
}

El primer que veieu és anchors. El sistema d'ancoratge de QtQuick proporciona una forma útil d'assegurar-vos que els components estiguin col·locats en certes parts d'un component pare. Hem ancorat la nostra GridLayout a l'esquerra, a dalt i a la dreta de la targeta principal, assegurant que el nostre contingut s'estengui per tota la targeta.

A continuació, especificarem l'espai entre les files i les columnes dins la nostra quadrícula, de manera que els nostres components no s'amunteguin. El Kirigami proporciona una sèrie d'útils unitats predefinides per a utilitzar amb aquest propòsit:

Unitat de KirigamiPíxels
smallSpacing4px
largeSpacing8px
gridUnit18px

Com recordareu, root és l'identificador de la nostra Kirigami.ApplicationWindow. Proporciona la propietat wideScreen, utilitzada per a determinar si la pantalla del dispositiu actual és una pantalla ampla (és a dir, un monitor d'ordinador o un telèfon apaïsat). Aquí utilitzem un condicional ternari per a variar el nombre de columnes a la nostra quadrícula depenent de la pantalla que utilitzem: si és una pantalla ampla, la quadrícula tindrà 4 columnes, en cas contrari en tindrà 2.

Components interns

Podríem crear tres etiquetes dins del nostre component delegat i cridar-les un dia. Però això no es veuria particularment bonic. Farem ús d'alguns components més adequats:

GridLayout {
    // ...
    Kirigami.Heading {
        Layout.fillHeight: true
        level: 1
        text: date
    }

    ColumnLayout {
        Kirigami.Heading {
            Layout.fillWidth: true
            level: 2
            text: name
        }

        Kirigami.Separator {
            Layout.fillWidth: true
            visible: description.length > 0
        }

        Controls.Label {
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            text: description
            visible: description.length > 0
        }
    }

    Controls.Button {
        Layout.alignment: Qt.AlignRight
        Layout.columnSpan: 2
        text: i18n("Edit")
    }
}
Com es veu la targeta personalitzada

Com es veu la targeta personalitzada

  • A l'esquerra, Kirigami.Heading: utilitza la date d'un ListElement com una capçalera de nivell 1.
  • Al mig, ColumnLayout: té una Kirigami.Heading que mostra el nom de la tasca; un Kirigami.Separator, el qual proporciona la línia en horitzontal; i un Controls.Label, que mostra una descripció opcional de la tasca. Els dos últims components tenen una propietat visible, la qual comprova si la descripció està buida o no, i mostra els components segons el resultat de description.length > 0.
  • A la dreta, Controls.Button: un botó que farà alguna cosa... aviat!

La nostra aplicació fins ara

Main.qml:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Controls
import org.kde.kirigami as Kirigami

Kirigami.ApplicationWindow {
    id: root

    width: 400
    height: 300

    title: i18nc("@title:window", "Day Kountdown")

    // ListModel needed for ListView, contains elements to be displayed
    ListModel {
        id: kountdownModel
        // Each ListElement is an element on the list, containing information
        ListElement {
            name: "Dog birthday!!"
            description: "Big doggo birthday blowout."
            date: 100
        }
    }

    Component {
        id: kountdownDelegate
        Kirigami.AbstractCard {
            contentItem: Item {
                implicitWidth: delegateLayout.implicitWidth
                implicitHeight: delegateLayout.implicitHeight
                GridLayout {
                    id: delegateLayout
                    anchors {
                        left: parent.left
                        top: parent.top
                        right: parent.right
                    }
                    rowSpacing: Kirigami.Units.largeSpacing
                    columnSpacing: Kirigami.Units.largeSpacing
                    columns: root.wideScreen ? 4 : 2

                    Kirigami.Heading {
                        // Level determines the size of the heading
                        level: 1
                        text: date
                    }

                    // Layout for positioning elements vertically
                    ColumnLayout {
                        Kirigami.Heading {
                            Layout.fillWidth: true
                            level: 2
                            text: name
                        }
                        // Horizontal rule
                        Kirigami.Separator {
                            Layout.fillWidth: true
                            visible: description.length > 0
                        }
                        // Labels contain text
                        Controls.Label {
                            Layout.fillWidth: true
                            // Word wrap makes text stay within box and shift with size
                            wrapMode: Text.WordWrap
                            text: description
                            visible: description.length > 0
                        }
                    }
                    Controls.Button {
                        Layout.alignment: Qt.AlignRight
                        // Column spanning within grid layout (vertically in this case)
                        Layout.columnSpan: 2
                        text: i18n("Edit")
                        //onClicked: to be done...
                    }
                }
            }
        }
    }

    pageStack.initialPage: Kirigami.ScrollablePage {
        title: i18nc("@title", "Kountdown")

        // List view for card elements
        Kirigami.CardsListView {
            id: cardsView
            // Model contains info to be displayed
            model: kountdownModel
            // Delegate is how the information will be presented in the ListView
            delegate: kountdownDelegate
        }
    }
}

Captura de pantalla de l'aparença de l'aplicació després de completar aquesta lliçó

Així que aquí està la nostra targeta bàsica!

Amb aquests passos, hem establert les bases bàsiques per a afegir tota la funcionalitat a la nostra aplicació.