Layouter, listvyer och kort

Räkna ut de olika sätten att placera saker på en sida

Placera ut ditt innehåll

Nu när vi förstår hur sidor fungerar, är det dags att lägga till saker i våran. Vi går igenom ett antal viktiga layoutkomponenter och element som är användbara när vi konstruerar vårt program.

Bli inte rädd för de stora kodstyckena. Vi går igenom allting som vi inte har täckt tidigare, och i slutet av avsnittet kommer du att ha ett snyggt program.

Listvyer

Om du någonsin har använt Discover, NeoChat eller Plasmas systeminställningar, har du stött på en listvy. Listvyer låter dig helt enkelt visa data från en lista.

Kirigami.CardsListView {
    id: layout
    model: kountdownModel
    delegate: kountdownDelegate
}

Det verkar kryptiskt, men oroa dig inte. Låt oss börja uppifrån.

Det första du märker är att vi använder Kirigami.CardsListView. Det är en listvy som låter oss enkelt visa kort i en lista. Dock är listvyer gjorda för att visa data tagen från en modell, för att automatiskt befolkas från en uppsättning data som vi pekar den på. Det är där egenskapen model kommer in: i det här exemplet pekar den på kountdownModel.

Modell

ListModel {
    id: kountdownModel
    // Varje ListElement är ett element i listan, och innehåller information
    ListElement { name: "Dog birthday!!"; description: "Big doggo birthday blowout."; date: 100 }
}

En modell definierar sättet som en datapost är strukturerad. Genom att titta på vårt listelement ovan kan vi se hur element i kountdownModel är strukturerade: de innehåller ett namn, en beskrivning och ett datum. De två första är bara strängar, och den tredje är ett tal som vi använder som platsmarkering.

Modeller är också användbara på det sätt de kan ändras genom användning av flera metoder. Några viktiga är:

  • ListModelName.append(jsobject ditt_objekt) lägger till ett Javascript-objekt som du tillhandahåller i listmodellen, och placerar det efter det sista objektet i modellen. För att det ska göras korrekt, måste du tillhandahålla ett Javascript-objekt med korrekta egenskaper och motsvarande datatyper.
  • ListModelName.get(int index) returnerar JSObject vid indexplatsen du tillhandahåller.
  • ListModelName.remove(int index, int count) tar bort JSObject på den tillhandahållna indexplatsen, och så många efter den indexplatsen som du vill (1 inkluderar bara JSObject på tillhandahållet index)
  • ListModelName.set(int index, jsobject yourobject) ändrar objektet på tillhandahållen indexplats med värdet som tillhandahålls i yourobject. Samma regel som med .append.

Delegera

Delegaten hanterar hur data i ListModel visas i listvyn. CardsListView element är konstruerade med delegater av korttyp i åtanke, och vi har verkligen använd ett Kirigami.AbstractCard element som vår delegat i utdraget ovan.

Delegater får automatiskt egenskaperna i listelementen som vi har angivit i vår modell. Vi kan därför bara referera till egenskaperna name, description och date för våra listelement som om de var konventionella variabler inne i vår delegat.

Bygga vårt delegatkort

Component {
    id: kountdownDelegate
    Kirigami.AbstractCard {
        contentItem: Item {
            // implicitWidth/Height definierar den naturliga bredd/höjd för ett objekt om ingen
            // bredd eller höjd anges. Inställningen nedan definierar en komponents föredragna
            // storlek baserat på dess innehåll
            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 < 100000) ? date : i18n("%1 days", Math.round((date-Date.now())/86400000))
                }

                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: snart klart!
                }
            }
        }
    }
}

implicitWidth och implicitHeight

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

Om vi betraktar vår Kirigami.AbstractCard, är den första egenskaperna vi ställer in implicitWidth och implicitHeight. Vi har ställer in dem till delegateLayout.implicitWidth och delegateLayout.implicitHeight, dvs. implicitWidth och implicitHeight i elemented GridLayout. Implicita bredder och höjder är egenskaper som ställs in som en sorts förvalda värden, dvs. om det inte finns någon bredd och höjd inställt för komponenterna. Vi har därför ställt in implicitWidth och implicitHeight i vår Kirigami.AbstractCard till det av vår GridLayout under, för att säkerställa att vår GridLayout inte flyter ut utanför kortet.

Layouter

GridLayout är inne i komponenten Item vi tillhandahåller för egenskapen contentItem. Det är objektet som innehåller det vi kommer att visa på kortet.

Vi behöver också välja en layout för våra komponenter så att de inte bara staplas upp på varandra. Det finns tre huvudtyper vi kan välja bland:

  • ColumnLayout placerar ut komponenterna vertikalt, i en enda kolumn
  • RowLayout placerar ut komponenterna horisontellt, i en enda rad
  • GridLayout placerar ut komponenterna i ett rutnät med en sammansättning du väljer

Allt vi behöver göra med ColumnLayout and RowLayout är att skriva våra komponenter inne i komponenten Layout. Som du kan se, bestämde vi oss för en rutnätslayout, vilket medför lite mer hantverk.

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
    ...
}

Det första du ser är vår anchor. Förankringssystemet i QtQuick är ett användbart sätt att säkerställa att komponenterna är placerade på vissa ställen i en överliggande komponent. Vi har förankrat vår GridLayout till vänster, överst och till höger om det överliggande kortet, vilket säkerställer att vårt innehåll sträcker sig över hela kortet.

Därefter anger vi mellanrummet mellan raderna och kolumnerna i vårt rutnät, så att våra komponenter inte trycks ihop. Kirigami tillhandahåller ett antal praktiska fördefinierade enheter att använda i detta syfte:

Kirigami-enhetBildpunkter
smallSpacing4 bildpunkter
largeSpacing8 bildpunkter
gridUnit18 bildpunkter

Vi har också använt ett villkor här för att variera antal kolumner i vårt rutnät beroende på skärmen vi använder. Om vi använder en bred skärm (dvs. en datorskärm eller en telefon med liggande format) har rutnätet fyra kolumner, annars har det två.

Inre komponenter

Vi skulle kunna bara skapa tre beteckningar inne i vår delegatkomponent och låta det vara nog. Men det skulle inte se särskilt snyggt ut.

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")
    }
}

  • Vänster, Kirigami.Heading: använder date i ListElement som första nivåns rubrik.
  • Mitten, ColumnLayout: har en Kirigami.Heading som visar aktivitetsnamnet, en Kirigami.Separator som tillhandahåller den horisontella linjen, och en Controls.Label som visar en uppgifts valfria beskrivning. De senare två komponenterna har egenskapen visible som kontrollerar om beskrivningen är tom eller inte och visar komponenten beroende på resultatet av description.length > 0.
  • Just det, Controls.Button: en knapp som gör något ... snart.

Vårt program så långt

Så där är vårt grundläggande kort.

Med de här stegen har vi nu lagt grundstenen för att lägga till alla funktioner i vårt program.