Layouts, ListViews e Cards
Dispondo seu conteúdo
Agora que entendemos como as páginas funcionam, é hora de adicionar elementos a elas. Abordaremos uma série de componentes e elementos de layout importantes que serão úteis ao projetar nosso aplicativo.
Ao final desta seção, você terá um aplicativo com uma aparência bacana.
ListViews
Se você já usou o Discover, o NeoChat ou as Configurações do Sistema do Plasma, você já deve ter se deparado com uma ListView. Simplificando, ela permite que você exiba dados em uma lista.
pageStack.initialPage: Kirigami.ScrollablePage {
// ...
Kirigami.CardsListView {
id: cardsView
model: kountdownModel
delegate: kountdownDelegate
}
}Parece enigmático, mas não se preocupe. Vamos começar do começo.
Adicionamos este componente dentro do nosso Kirigami.ScrollablePage do último tutorial.
Estamos usando Kirigami.CardsListView, que é uma ListView que nos permite exibir facilmente cartões em uma lista. No entanto, ListViews são feitas para mostrar dados retirados de um modelo - para se preencherem automaticamente com um conjunto de dados para o qual apontamos. É aí que entra a propriedade model: neste exemplo, ela aponta para kountdownModel.
Modelo
Kirigami.ApplicationWindow {
// ...
ListModel {
id: kountdownModel
// Cada ListElement é um elemento da lista, contendo informações
ListElement {
name: "Dog birthday!!"
description: "Big doggo birthday blowout."
date: 100
}
}
// ...
}Adicionamos nosso kountdownModel dentro do Kirigami.ApplicationWindow do último tutorial.
Um modelo define a maneira como uma entrada de dados é estruturada. Nosso kountdownModel consistirá em apenas um elemento por enquanto. Observando nosso ListElement acima, podemos ver como os dados do nosso kountdownModel são estruturados: ele contém um nome, uma descrição e uma data. Isso não é imutável, e você pode ter diferentes tipos de dados em seu modelo. Os dois primeiros são apenas strings, e o terceiro é um número que estamos usando como espaço reservado.
Nota
Como o QML é construído sobre JavaScript, muitos dos recursos dessa linguagem estão disponíveis para uso em arquivos QML. No entanto, as variáveis QML precisam ser prefixadas comproperty, a menos que estejam dentro de um bloco de código JS. Você pode ler mais sobre isso nesta página.Os modelos também são úteis na forma como podem ser modificados através do uso de vários métodos. Alguns importantes são:
- ListModel.append(yourobject: jsobject) adiciona um objeto JavaScript (JSObject)
yourobjectao ListModel e o coloca após o último item do modelo. Para que isso aconteça corretamente, você deve fornecer um JSObject com as propriedades corretas e os tipos de dados correspondentes. - ListModel.get(index: int) retorna o JSObject no local do índice que você fornecer.
- ListModel.remove(index: int, count: int) remove o JSObject no local
indexfornecido e tantos outros depois desse local de índice quantos você colocar emcount(1 inclui apenas o JSObject no índice fornecido) - ListModel.set(index: int, yourobject: jsobject) altera o item no local
indexfornecido com os valores fornecidos emyourobject. Mesmas regras deappend().
Delegados
Enquanto nosso kountdownModel contém os dados que serão exibidos, nosso kountdownDelegate controlará como os dados serão exibidos na ListView. Para isso, usamos uma Kirigami.CardsListView projetada para exibir delegados do tipo cartão, e esses delegados serão representados visualmente por meio de um Kirigami.AbstractCard.
Os delegados recebem automaticamente as propriedades das instâncias do ListElement que especificamos em nosso modelo. Portanto, podemos simplesmente nos referir às suas propriedades nome, descrição e data como se fossem variáveis convencionais em nosso delegado.
Construindo o cartão delegado
O Componente que representará nosso delegado pode ser adicionado dentro do nosso Kirigami.ApplicationWindow. Em seguida, verificaremos o que cada parte do nosso componente delegado faz.
Kirigami.ApplicationWindow {
// ...
Component {
id: kountdownDelegate
Kirigami.AbstractCard {
contentItem: Item {
// implicitWidth/Height define a largura/altura natural de um item se nenhuma
// largura ou altura for especificada. A configuração abaixo define o tamanho
// preferencial de um componente com base em seu conteúdo
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: 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: a ser feito... em breve!
}
}
}
}
}
// ...
}implicitWidth e implicitHeight
A primeira parte que veremos é como gerenciar a largura e a altura do nosso componente:
Kirigami.AbstractCard {
contentItem: Item {
implicitWidth: delegateLayout.implicitWidth
implicitHeight: delegateLayout.implicitHeight
GridLayout {
id: delegateLayout
// ...
}
}
}Observando nosso Kirigami.AbstractCard, as primeiras propriedades que definimos são implicitWidth e implicitHeight. Definimos essas propriedades como delegateLayout.implicitWidth e delegateLayout.implicHeight, ou seja, o implicitWidth e implicitHeight do elemento GridLayout.
Larguras e alturas implícitas são propriedades disponíveis em qualquer Item que funcionam como dicas e são definidas como padrão, ou como alternativa, caso não haja largura ou altura explícitas definidas para esses componentes. Esses valores são, por padrão, 0x0, portanto, é muito importante que você os defina nos componentes brutos do Item, conforme feito acima.
Aqui, definimos implicitWidth e implicitHeight do nosso Kirigami.AbstractCard como o do GridLayout abaixo para garantir que ele não transborde do cartão. Dessa forma, o cartão ocupa o espaço necessário para o seu conteúdo.
Layouts
O GridLayout está dentro do componente Item que fornecemos para a propriedade contentItem. Este é o Item que contém o que será exibido no seu cartão.
Também precisamos escolher um layout para nossos componentes, para que eles não fiquem empilhados uns sobre os outros. Há três tipos principais que podemos escolher:
- ColumnLayout dispõe seus componentes verticalmente, em uma única coluna
- RowLayout dispõe seus componentes horizontalmente, em uma única linha
- GridLayout dispõe seus componentes em uma grade com uma composição de sua escolha
Com ColumnLayout e RowLayout, tudo o que precisamos fazer é escrever nossos componentes dentro do componente Layout. Como você pode ver, optamos por um GridLayout, o que requer um pouco mais de trabalho 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
// ...
}A primeira coisa que você vê são nossas âncoras. O sistema de ancoragem do QtQuick fornece uma maneira útil de garantir que seus componentes estejam posicionados em determinadas partes de um componente pai. Ancoramos nosso GridLayout à esquerda, superior e direita do cartão pai, garantindo que nosso conteúdo se estenda por todo o cartão.
Em seguida, especificamos o espaçamento entre as linhas e colunas em nossa grade, para que nossos componentes não fiquem amontoados. O Kirigami fornece uma série de unidades predefinidas úteis para esse propósito:
| Unidades do Kirigami | Pixels |
|---|---|
| smallSpacing | 4px |
| largeSpacing | 8px |
| gridUnit | 18px |
Nota
O Grupo de Design Visual (VDG, sigla em inglês) do KDE tem muito mais informações sobre as diferentes unidades definidas no Plasma e no Kirigami nas Diretrizes de Interface Humana.Como você deve se lembrar, root é o id do nosso Kirigami.ApplicationWindow. Ele fornece a propriedade wideScreen, usada para determinar se a tela atual do dispositivo é widescreen (ou seja, um monitor de computador ou um telefone em modo paisagem). Usamos um condicional ternário aqui para variar o número de colunas em nossa grade dependendo da tela que estamos usando: se for widescreen, a grade terá 4 colunas, caso contrário, terá 2.
Componentes internos
Poderíamos criar apenas três rótulos dentro do nosso componente delegado e encerrar o dia, mas isso não ficaria muito bom. Usaremos alguns componentes mais convenientes:
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")
}
}
Como o cartão personalizado se parece
- À esquerda, Kirigami.Heading: usa a
datadoListElementcomo um título de nível 1. - No meio, ColumnLayout: possui um Kirigami.Heading que exibe o nome da tarefa; um Kirigami.Separator, que fornece a linha horizontal; e um Controls.Label, que exibe a descrição opcional da tarefa. Os dois últimos componentes têm uma propriedade visible, que verifica se a descrição está vazia ou não e exibe os componentes dependendo do resultado de
description.length > 0. - À direita, Controls.Button: um botão que fará algo... em breve!
Nosso aplicativo até agora
Main.qml:
| |

Então aqui está o nosso cartão básico!
Com essas etapas, estabelecemos a base para adicionar todas as funcionalidades ao nosso aplicativo.