Układy, widoki list oraz karty

Poznawanie różnych sposobów na umieszczanie rzeczy na stronie

Układanie twojej treści

Teraz, gdy już rozumiemy jak działają strony, pora na dodanie naszych rzeczy. Przejdziemy przez kilka ważnych składników układów i rzeczy, które będą użyteczne przy tworzeniu naszej aplikacji.

Niech nie przeraża cię duża ilość kodu! Przejdziemy przez wszystko, o czym jeszcze nie mówiliśmy, a na końcu tego rozdziału będziesz miał ładnie wyglądającą aplikację.

ListViews

Jeśli kiedykolwiek używałeś Odkrywcy, NeoChat lub Ustawień Systemowych Plazmy, to na pewno widziałeś widok ListView. Ujmując to prosto, widoki ListViews umożliwiają ci wyświetlanie danych na liście.

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

Wydaje się to tajemnicze, lecz nie przejmuj się. Zacznijmy od góry.

Pierwszą rzeczą jaką zauważysz, jest to, że używamy Kirigami.CardsListView. Jest to ListView, który umożliwia nam łatwe wyświetlanie kart na liście. Jednakże, ListViews są stworzone do wyświetlanie danych pobranych z modelu - aby samoczynnie wypełnić się zestawem danych, na który wskażemy. To miejsce, w którym do gry wchodzi właściwość model: w tym przykładzie wskazuje ona na kountdownModel.

Model

ListModel {
    id: kountdownModel
    // Każdy ListElement jest rzeczą na liście, która zawiera pewne dane
    ListElement { name: "Dog birthday!!"; description: "Big doggo birthday blowout."; date: 100 }
}

Tryb modalny określa sposób w jaki wpisywanie danych jest ułożone. Patrząc na nasz ListElement powyżej możemy zobaczyć w jaki sposób rzeczy kountdownModel są ułożone: zawierają nazwę, opis oraz datę. Pierwsze dwa są tylko ciągani znaków, a trzeci jest liczbą, która używamy jako pole zastępcze.

Modele są także użyteczne w sposób w jaki można je zmieniać poprzez użycie kilku metod. Jedne z ważniejszych to:

  • ListModelName.append(jsobject yourobject) dodaje obiekt JavaScript, który dostarczysz do ListModel i umieszcza go za ostatnią rzeczą w modelu. Aby odbyło się to poprawnie, musisz dostarczyć obiekt JavaScript z poprawnymi właściwościami i odpowiednimi rodzajami danych.
  • ListModelName.get(int index) zwraca JSObject w miejscu indeksu, który podasz.
  • ListModelName.remove(int index, int count) usuwa JSObject w miejscu wskazanym przez index i tyle obiektów po nim ile chcesz (1 oznacza tylko jeden JSObject wskazany przez index)
  • ListModelName.set(int index, jsobject twójObiekt) zmienia rzecz pod podanym indeksem na wartości podane w twójObiekt. Te same zasady co z .append.

Deleguj

Delegat określa jak dane naszego modelu ListModel będą wyświetlane na widoku ListView. Rzeczy z CardsListView zostały stworzone dla delegatów o rodzaj karty i rzeczywiście użyliśmy rzeczy Kirigami.AbstractCard w naszym delegacie w wycinku powyżej.

Delegaci samoczynnie otrzymują właściwości ListElements, które określiliśmy w naszym modelu. Stąd możemy odnosić się do właściwości name, description oraz date naszej ListElements tak, jakby były zwykłymi zmiennymi w naszym delegacie.

Budowanie naszej karty delegata

Component {
    id: kountdownDelegate
    Kirigami.AbstractCard {
        contentItem: Item {
            // implicitWidth/Height określają naturalną szerokość/wysokość rzeczy, jeśli nie
            // podano szerokości lub wysokości. Ustawienie poniżej określa optymalny rozmiar
            // składnika na podstawie jego treści
            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: do zrobienia... w krótce!
                }
            }
        }
    }
}

implicitWidth oraz implicitHeight

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

Patrząc na nasze Kirigami.AbstractCard, pierwszymi właściwościami, które nadamy, to implicitWidth oraz implicitHeight. Ustawiliśmy je na delegateLayout.implicitWidth oraz delegateLayout.implicitHeight, tj. implicitWidth oraz implicitHeight rzeczy GridLayout. Niejednoznacznie podane szerokości i wysokości są rodzajem domyślnych wartości, tj. nie ma podanej jednoznacznie szerokości i wysokości dla tych składników. Stąd ustawiliśmy implicitWidth oraz implicitHeight naszej Kirigami.AbstractCard na tę z GridLayout poniżej, aby upewnić się, że GridLayout nie wyleje naszej karty.

Układy

GridLayout znajduje się wewnątrz składnika Item, który podaliśmy we właściwości contentItem. Jest to rzecz, która zawiera to, co będzie wyświetlane w naszej karcie.

Musimy także wybrać układ dla naszych składników tak, aby nie zgrupowały się jeden na drugim. Istnieją trzy różne rodzaje, z których możemy wybrać:

  • ColumnLayout układa twoje składniki w pionie, w pojedynczej kolumnie
  • RowLayout układa twoje składniki w poziomie, w pojedynczym wierszu
  • GridLayout układa składnik w siatkę z układem taki jaki sobie wybierzesz

Z ColumnLayout oraz RowLayout jedyne, co musimy zrobić to wpisać nasze składniki do wewnątrz składnika Layout. Jak widzisz, wybraliśmy układ siatki, który wymaga trochę więcej pracy ręcznej.

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

Pierwszą rzeczą jaką zobaczysz jest nasza anchor. System kotwiczenia QtQuick dostarcza użytecznych sposobów na zapewnienie, że rzeczy są umieszczone w pewnych częściach rzeczy nadrzędnej. Zakotwiczyliśmy nasz GridLayout do lewe, do góry i dp prawej naszej karty nadrzędnej, upewniając się, że nasza treść rozciągnie się na całą kartę.

Następnie określamy odstępy pomiędzy wierszami i kolumnami w naszej siatce, tak aby nasze składniki nie zgrupowały się. Kirigami dostarcza kilka przydatnych jednostek do tego celu:

| Kirigami Unit | Pixels | | —— | —— | | smallSpacing | 4px | | largeSpacing | 8px | | gridUnit | 18px |

Użyliśmy tutaj także warunku, aby zmienić liczbę kolumn na naszej siatce w zależności od ekranu, którego używamy. Jeśli używamy szerokiego ekranu (tj. monitora komputerowego lub telefonu w ustawieniu poziomym) to siatka będzie miała 4 kolumny, w przeciwnym przypadku 2.

Składniki wnętrza

Moglibyśmy stworzyć trzy etykiety wewnątrz naszego składnika delegata i powiedzieć to wszystko. Nie wyglądałoby to jednak szczególnie ładnie.

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

  • Po lewej, Kirigami.Heading: używa date rzeczy ListElement na 1 poziomie nagłówka.

  • Środkowe, ColumnLayout: ma Kirigami.Heading, który wyświetla nazwę zadania; Kirigami.Separator, który stanowi linię w poziomie; oraz Controls.Label, który wyświetla dobrowolny opis zadania. Dwa drugie składniki mają właściwość visible, która sprawdza, czy opis jest pusty, czy nie i wyświetla składniki w zależności od wyniku description.length > 0.

  • Po prawej, Controls.Button: przycisk, który będzie coś robić… wkrótce!

Nasza aplikacja w tej chwili

Tak więc jest to nasza podstawowa karta!

Tymi krokami, stworzyliśmy fundament do dodawania całej funkcjonalności do naszej aplikacji.