Exemplos Práticos de ListView com QML
Este guia explora diversas implementações do componente ListView em QML, demonstrando como criar interfaces de usuário dinâmicas e interativas.
- Barra de Navegação Vertical Simples
Este exemplo cria uma barra de navegação vertical estática com itans de texto clicáveis.
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
width: 100
height: parent.height
color: "grey"
ListView {
id: navigationList
width: 90
height: parent.height
x: 10
y: 10
spacing: 10
clip: true
model: ['Início', 'Dados de Vendas', 'Gestão de Estoque', 'Planejamento de Frete', 'Publicidade', 'Configurações', 'Permissões de Usuário']
delegate: Rectangle {
width: 80
height: 30
color: 'transparent' // Cor padrão, pode ser alterada em outras partes
radius: 10
Text {
id: itemLabel
anchors.centerIn: parent
color: 'white'
text: modelData
font.pointSize: 10
font.bold: true
font.family: "Arial" // Fonte alternativa
}
MouseArea {
anchors.fill: parent
onClicked: {
navigationList.currentIndex = index
}
}
}
highlight: Rectangle {
color: "transparent"
border.width: 2
border.color: 'white'
radius: 10
}
highlightMoveDuration: 0
}
}
- Barra de Navegação Horizontal
Este exemplo demonstra um menu de navegação horizontal com itens clicáveis e destaque visual.
import QtQuick 2.15
import QtQuick.Controls 2.15
Rectangle {
width: parent.width
height: 38
color: 'black'
ListView {
id: horizontalNav
x: 10
anchors.verticalCenter: parent.verticalCenter
width: parent.width
height: 30
model: ['Página Inicial', 'Ferramentas', 'Loja', 'Relatório de Desempenho', 'Anúncios', 'Estoque', 'Relatório Financeiro', 'Análise de Produtos', 'Ajustes']
clip: true
delegate: Rectangle {
width: 60
height: 30
color: 'transparent'
radius: 10
Text {
text: modelData
color: "white"
anchors.centerIn: parent
font.bold: true
font.pointSize: 8
font.family: "Verdana"
}
MouseArea {
anchors.fill: parent
onClicked: {
horizontalNav.currentIndex = index
}
}
}
spacing: 10
highlight: Rectangle {
color: "transparent"
border.width: 3
border.color: "yellow"
radius: 10
}
highlightMoveDuration: 0
orientation: ListView.Horizontal
}
}
- Exibindo Dados Estruturados com ListModel
Este exemplo utiliza um ListModel para gerenciar dados com múltiplos papéis (roles) e exibi-los em um ListView, com destaque para o item selecionado.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Rectangle {
width: 360
height: 300
color: "#EEEEEE"
Component {
id: productDelegate
Item {
id: itemWrapper
width: parent.width
height: 30
MouseArea {
anchors.fill: parent
onClicked: itemWrapper.ListView.view.currentIndex = index
}
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text {
id: nameText
text: name
color: itemWrapper.ListView.isCurrentItem ? "red" : "black"
font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18
Layout.preferredWidth: 120
}
Text {
text: price
color: itemWrapper.ListView.isCurrentItem ? "red" : "black"
font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18
Layout.preferredWidth: 80
}
Text {
text: manufacturer
color: itemWrapper.ListView.isCurrentItem ? "red" : "black"
font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18
Layout.fillWidth: true
}
}
}
}
ListView {
id: productListView
anchors.fill: parent
delegate: productDelegate
model: ListModel {
id: productModel
ListElement { name: "iPhone 3GS"; price: "1000"; manufacturer: "Apple" }
ListElement { name: "iPhone 4"; price: "1800"; manufacturer: "Apple" }
ListElement { name: "iPhone 4S"; price: "2300"; manufacturer: "Apple" }
ListElement { name: "iPhone 5"; price: "4900"; manufacturer: "Apple" }
ListElement { name: "B199"; price: "1590"; manufacturer: "HuaWei" }
ListElement { name: "MI 2S"; price: "1999"; manufacturer: "XiaoMi" }
ListElement { name: "GALAXY S5"; price: "4699"; manufacturer: "Samsung" }
}
focus: true
highlight: Rectangle { color: "lightblue" }
}
}
- ListView com Cabeçalho, Rodapé e Interação
Este exemplo expande o anterior, adicionando um cabeçalho, um rodapé dinâmico e a capacidade de interagir com os itens (seleção e exclusão por duplo clique).
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Rectangle {
width: 360
height: 300
color: "#EEEEEE"
Component {
id: header
Item {
width: parent.width
height: 30
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text { text: "Nome"; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
Text { text: "Preço"; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
Text { text: "Fab."; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
}
}
}
Component {
id: itemDelegate
Item {
id: itemWrapper
width: parent.width
height: 30
MouseArea {
anchors.fill: parent
onClicked: {
itemWrapper.ListView.view.currentIndex = index
console.log("Índice selecionado:", index)
}
onDoubleClicked: {
itemWrapper.ListView.view.model.remove(index)
}
}
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text { text: name; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
Text { text: price; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
Text { text: manufacturer; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
}
}
}
Component {
id: footer
Text {
width: parent.width
font.italic: true
font.bold: true
font.pointSize: 8
color: "blue"
height: 30
verticalAlignment: Text.AlignVCenter
}
}
ListView {
id: productListView
anchors.fill: parent
delegate: itemDelegate
header: header
footer: footer
model: ListModel {
id: productModel
ListElement { name: "iPhone 3GS"; price: "1000"; manufacturer: "Apple" }
ListElement { name: "iPhone 4"; price: "1800"; manufacturer: "Apple" }
ListElement { name: "iPhone 4S"; price: "2300"; manufacturer: "Apple" }
ListElement { name: "iPhone 5"; price: "4900"; manufacturer: "Apple" }
ListElement { name: "B199"; price: "1590"; manufacturer: "HuaWei" }
ListElement { name: "MI 2S"; price: "1999"; manufacturer: "XiaoMi" }
ListElement { name: "GALAXY S5"; price: "4699"; manufacturer: "Samsung" }
}
focus: true
highlight: Rectangle { color: "lightblue" }
onCurrentIndexChanged: {
if (currentIndex >= 0) {
var data = model.get(currentIndex)
footerItem.text = `${data.name}, ${data.price}, ${data.manufacturer}`
} else {
footerItem.text = ""
}
}
}
}
- Gerenciamento de Dados (Adicionar, Remover, Limpar)
Este exemplo adiciona funcionalidades para adicionar novos itens, remover itens selecionados e limpar toda a lista, além de exibir informações no rodapé.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Rectangle {
width: 360
height: 300
color: "#EEEEEE"
Component {
id: headerView
Item {
width: parent.width
height: 30
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text { text: "Nome"; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
Text { text: "Preço"; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
Text { text: "Fab."; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
}
}
}
Component {
id: itemDelegate
Item {
id: itemWrapper
width: parent.width
height: 30
MouseArea {
anchors.fill: parent
onClicked: {
itemWrapper.ListView.view.currentIndex = index
mouse.accepted = true
}
onDoubleClicked: {
itemWrapper.ListView.view.model.remove(index)
mouse.accepted = true
}
}
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text { text: name; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
Text { text: price; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
Text { text: manufacturer; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
}
}
}
Component {
id: footerView
Item {
id: footerRootItem
width: parent.width
height: 30
property alias text: statusText.text
signal clearAll()
signal addItem()
Button {
id: clearButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: "Limpar"
onClicked: footerRootItem.clearAll()
}
Button {
id: addButton
anchors.right: clearButton.left
anchors.rightMargin: 4
anchors.verticalCenter: parent.verticalCenter
text: "Adicionar"
onClicked: footerRootItem.addItem()
}
Text {
id: statusText
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
font.italic: true
font.bold: true
font.pointSize: 8
color: "blue"
height: 30
}
}
}
ListView {
id: productListView
anchors.fill: parent
delegate: itemDelegate
header: headerView
footer: footerView
model: ListModel {
id: productModel
ListElement { name: "iPhone 3GS"; price: "1000"; manufacturer: "Apple" }
ListElement { name: "iPhone 4"; price: "1800"; manufacturer: "Apple" }
ListElement { name: "iPhone 4S"; price: "2300"; manufacturer: "Apple" }
ListElement { name: "iPhone 5"; price: "4900"; manufacturer: "Apple" }
ListElement { name: "B199"; price: "1590"; manufacturer: "HuaWei" }
ListElement { name: "MI 2S"; price: "1999"; manufacturer: "XiaoMi" }
ListElement { name: "GALAXY S5"; price: "4699"; manufacturer: "Samsung" }
}
focus: true
highlight: Rectangle { color: "lightblue" }
onCurrentIndexChanged: {
if (currentIndex >= 0) {
var data = model.get(currentIndex)
footerItem.text = `${data.name}, ${data.price}, ${data.manufacturer}`
} else {
footerItem.text = ""
}
}
function addNewItem() {
model.append({
"name": "MX3",
"price": "1799",
"manufacturer": "MeiZu"
})
}
Component.onCompleted: {
footerItem.clearAll.connect(model.clear)
footerItem.addItem.connect(addNewItem)
}
}
}
- Animações e Movimentação de Itens (Subir/Descer)
Este exemplo final demonstra o uso de transições animadas para adição, remoção e movimentação de itens dentro do ListView, incluindo funcionalidades para mover itens para cima e para baixo na lista.
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
Rectangle {
width: 500
height: 400
color: "#EEEEEE"
Component {
id: headerView
Item {
width: parent.width
height: 30
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text { text: "Nome"; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
Text { text: "Preço"; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
Text { text: "Fab."; font.bold: true; font.pixelSize: 20; Layout.preferredWidth: 120 }
}
}
}
Component {
id: itemDelegate
Item {
id: itemWrapper
width: parent.width
height: 30
MouseArea {
anchors.fill: parent
onClicked: {
itemWrapper.ListView.view.currentIndex = index
mouse.accepted = true
}
onDoubleClicked: {
itemWrapper.ListView.view.model.remove(index)
mouse.accepted = true
}
}
RowLayout {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Text { text: name; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
Text { text: price; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
Text { text: manufacturer; color: itemWrapper.ListView.isCurrentItem ? "red" : "black"; font.pixelSize: itemWrapper.ListView.isCurrentItem ? 22 : 18; Layout.preferredWidth: 120 }
}
}
}
Component {
id: footerView
Item {
id: footerRootItem
width: parent.width
height: 30
property alias text: statusText.text
signal clearAll()
signal addItem()
signal insertItem()
signal moveItemUp()
signal moveItemDown()
Button { id: clearButton; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter; text: "Limpar"; onClicked: footerRootItem.clearAll() }
Button { id: addButton; anchors.right: clearButton.left; anchors.rightMargin: 4; anchors.verticalCenter: parent.verticalCenter; text: "Adicionar"; onClicked: footerRootItem.addItem() }
Button { id: insertButton; anchors.right: addButton.left; anchors.rightMargin: 4; anchors.verticalCenter: parent.verticalCenter; text: "Inserir"; onClicked: footerRootItem.insertItem() }
Button { id: moveDownButton; anchors.right: insertButton.left; anchors.rightMargin: 4; anchors.verticalCenter: parent.verticalCenter; text: "Baixo"; onClicked: footerRootItem.moveItemDown() }
Button { id: moveUpButton; anchors.right: moveDownButton.left; anchors.rightMargin: 4; anchors.verticalCenter: parent.verticalCenter; text: "Cima"; onClicked: footerRootItem.moveItemUp() }
Text {
id: statusText
anchors.left: parent.left
anchors.top: moveUpButton.bottom // Posiciona abaixo dos botões
font.italic: true
font.bold: true
font.pointSize: 8
color: "blue"
height: 30
verticalAlignment: Text.AlignVCenter
}
}
}
ListView {
id: productListView
anchors.fill: parent
delegate: itemDelegate
header: headerView
footer: footerView
focus: true
highlight: Rectangle { color: "lightblue" }
model: ListModel {
id: productModel
ListElement { name: "iPhone 3GS"; price: "1000"; manufacturer: "Apple" }
ListElement { name: "iPhone 4"; price: "1800"; manufacturer: "Apple" }
ListElement { name: "iPhone 4S"; price: "2300"; manufacturer: "Apple" }
ListElement { name: "iPhone 5"; price: "4900"; manufacturer: "Apple" }
ListElement { name: "B199"; price: "1590"; manufacturer: "HuaWei" }
ListElement { name: "MI 2S"; price: "1999"; manufacturer: "XiaoMi" }
ListElement { name: "GALAXY S5"; price: "4699"; manufacturer: "Samsung" }
}
onCurrentIndexChanged: {
if (currentIndex >= 0) {
var data = model.get(currentIndex)
footerItem.text = `${data.name}, ${data.price}, ${data.manufacturer}`
} else {
footerItem.text = ""
}
}
populate: Transition { NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 1000 } }
add: Transition { ParallelAnimation { NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 1000 }; NumberAnimation { properties: "x,y"; from: 0; duration: 1000 } } }
displaced: Transition { SpringAnimation { property: "y"; spring: 3; damping: 0.1; epsilon: 0.25 } }
remove: Transition { SequentialAnimation { NumberAnimation { property: "y"; to: 0; duration: 600 }; NumberAnimation { property: "opacity"; to: 0; duration: 400 } } }
move: Transition { NumberAnimation { property: "y"; duration: 700; easing.type: Easing.InQuart } }
function addOne() {
model.append({ "name": "MX3", "price": "1799", "manufacturer": "MeiZu" })
}
function insertOne() {
var insertIndex = currentIndex < 0 ? 0 : currentIndex + 1
model.insert(insertIndex, { "name": "HTC One E8", "price": "2999", "manufacturer": "HTC" })
}
function moveUp() {
if (currentIndex > 0) {
model.move(currentIndex, currentIndex - 1, 1)
}
}
function moveDown() {
if (currentIndex !== -1 && currentIndex < model.count - 1) {
model.move(currentIndex, currentIndex + 1, 1)
}
}
Component.onCompleted: {
footerItem.clearAll.connect(model.clear)
footerItem.addItem.connect(addOne)
footerItem.insertItem.connect(insertOne)
footerItem.moveItemUp.connect(moveUp)
footerItem.moveItemDown.connect(moveDown)
}
ScrollBar.vertical: ScrollBar {}
}
}