Seu primeiro app: lista de tarefas
Vamos construir um app que abre, adiciona, navega e persiste — passo a passo, introduzindo cada peça na ordem da necessidade.
Exemplo completo (copiar, colar e rodar)
Uma versão enxuta e autocontida do essencial: um TextField + Button que adiciona itens a uma lista
virtual. Cole numa classe MainWindow e rode com gradlew :lwjgl3:run.
package com.exemplo;
import java.util.ArrayList;
import java.util.List;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
public class MainWindow extends Window
{
private final List<String> tarefas = new ArrayList<>();
private TextField campo;
private VirtualListView lista;
@Override
public void onShow() throws Exception
{
super.onShow();
setLayout(new VerticalLayout());
// linha de entrada: campo (absorve a largura) + botão (tamanho natural)
Component entrada = new Component();
entrada.setLayout(new HorizontalLayout());
campo = new TextField().caption("Nova tarefa");
Button add = new Button("Adicionar");
entrada.add(LAY_FILL, campo);
entrada.add(LAY_AUTO, add);
add(LAY_AUTO, entrada);
// lista virtual: cada célula tem 3x a altura da fonte
lista = new VirtualListView(Relative.ofFont(3f), new DataAdapter()
{
@Override public int getCount() { return tarefas.size(); }
@Override public Component createView() { return new Label(""); }
@Override public void bindView(Component view, int index) // view primeiro, depois index
{
((Label) view).setText(tarefas.get(index)); // cast direto
}
});
add(LAY_FILL, lista);
add.onAction(c -> adicionar());
}
private void adicionar()
{
String texto = campo.getText().trim();
if (texto.isEmpty())
{
Hint.show("Digite uma tarefa", 1500); // sempre 2 args
return;
}
tarefas.add(texto);
campo.clearText();
lista.notifyDataChanged(); // re-mede e repinta a lista
}
}
Os trechos abaixo mostram só o miolo (o que vai dentro de
onShow()). Para rodar, coloque-os no esqueletoMainWindowacima.
1. O esqueleto da tela (VerticalLayout)
public class TarefasScreen extends Window {
private final List<Tarefa> tarefas = new ArrayList<>(); // estado em campos da Window
private Component listaBox;
private TextField campo;
@Override public void onShow() throws Exception {
super.onShow();
setLayout(new VerticalLayout());
// linha de entrada (campo + botão)
Component entrada = new Component();
entrada.setLayout(new HorizontalLayout());
campo = new TextField().caption("Nova tarefa");
Button add = new Button("Adicionar");
entrada.add(LAY_FILL, campo); // campo absorve a largura
entrada.add(LAY_AUTO, add); // botão do tamanho do conteúdo
add(LAY_AUTO, entrada);
// área da lista, que absorve o resto da altura
listaBox = new Component();
listaBox.setLayout(new VerticalLayout());
add(LAY_FILL, listaBox);
add.onAction(c -> adicionar());
carregar(); // popula do banco
}
}
2. Adicionar e refletir na UI (sem binding reativo)
private void adicionar() {
String texto = campo.getText().trim();
if (texto.isEmpty()) return;
Tarefa t = new Tarefa(texto);
tarefas.add(t);
salvar(t); // persiste (passo 4)
campo.clearText();
renderizarLista(); // você atualiza a UI manualmente
}
private void renderizarLista() {
listaBox.removeAll();
for (Tarefa t : tarefas) {
Button item = new Button(t.titulo);
item.onAction(c -> new DetalheScreen(t).show()); // navegação (passo 3)
listaBox.add(LAY_AUTO, item);
}
listaBox.setValidLayout(false); // re-mede o layout
Component.repaint(); // pede novo frame
}
3. Navegação push/pop
public class DetalheScreen extends Window {
private final Tarefa tarefa;
public DetalheScreen(Tarefa t) { this.tarefa = t; } // dados pelo construtor
@Override public void onShow() throws Exception {
super.onShow();
setLayout(new VerticalLayout());
add(LAY_AUTO, new Label(tarefa.titulo));
Button voltar = new Button("Voltar");
voltar.onAction(c -> hide()); // pop
add(LAY_AUTO, voltar);
}
}
4. Persistência em SQLite (fora da paint thread)
private void carregar() {
new Thread(() -> {
List<Tarefa> carregadas = new ArrayList<>();
try (SqliteDb db = SqliteDb.open("tarefas.db")) {
db.exec("CREATE TABLE IF NOT EXISTS tarefa(id INTEGER PRIMARY KEY, titulo TEXT)");
try (SqliteCursor c = db.query("SELECT id, titulo FROM tarefa ORDER BY id")) {
while (c.next()) // getters 0-based
carregadas.add(new Tarefa(c.getInt(0), c.getString(1)));
}
} catch (Exception e) { Debug.handle(e); }
Component.onUI(() -> { // volta à paint thread
tarefas.clear(); tarefas.addAll(carregadas);
renderizarLista();
});
}).start();
}
private void salvar(Tarefa t) {
new Thread(() -> {
try (SqliteDb db = SqliteDb.open("tarefas.db")) {
db.execute("INSERT INTO tarefa(titulo) VALUES(?)", t.titulo); // bind 1-based
} catch (Exception e) { Debug.handle(e); }
}).start();
}
Duas regras de ouro do SQLite: bind de parâmetro é 1-based, getters do cursor são 0-based; e o banco roda fora da paint thread, com o resultado voltando via
Component.onUI(...).
O que você construiu
Um app que abre, adiciona, navega e persiste — e você já viu os pilares que o
curso learn/ aprofunda.
Próximos passos
Veja também
Esta página foi útil?Obrigado pelo feedback!