Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.
Tu primera app: lista de tareas
Vamos a construir una app que abre, añade, navega y persiste — paso a paso, introduciendo cada pieza en el orden de la necesidad.
Ejemplo completo (copiar, pegar y ejecutar)
Una versión reducida y autocontenida de lo esencial: un TextField + Button que añade elementos a una lista
virtual. Pégalo en una clase MainWindow y ejecuta con 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());
// fila de entrada: campo (absorbe el ancho) + botón (tamaño 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 celda tiene 3x la altura de la fuente
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 primero, después index
{
((Label) view).setText(tarefas.get(index)); // cast directo
}
});
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); // siempre 2 args
return;
}
tarefas.add(texto);
campo.clearText();
lista.notifyDataChanged(); // re-mide y repinta la lista
}
}
Los fragmentos de abajo muestran solo el meollo (lo que va dentro de
onShow()). Para ejecutar, colócalos en el esqueletoMainWindowde arriba.
1. El esqueleto de la pantalla (VerticalLayout)
public class TarefasScreen extends Window {
private final List<Tarefa> tarefas = new ArrayList<>(); // estado en campos de la Window
private Component listaBox;
private TextField campo;
@Override public void onShow() throws Exception {
super.onShow();
setLayout(new VerticalLayout());
// fila de entrada (campo + botón)
Component entrada = new Component();
entrada.setLayout(new HorizontalLayout());
campo = new TextField().caption("Nova tarefa");
Button add = new Button("Adicionar");
entrada.add(LAY_FILL, campo); // el campo absorbe el ancho
entrada.add(LAY_AUTO, add); // botón del tamaño del contenido
add(LAY_AUTO, entrada);
// área de la lista, que absorbe el resto de la altura
listaBox = new Component();
listaBox.setLayout(new VerticalLayout());
add(LAY_FILL, listaBox);
add.onAction(c -> adicionar());
carregar(); // puebla desde la base de datos
}
}
2. Añadir y reflejar en la UI (sin binding reactivo)
private void adicionar() {
String texto = campo.getText().trim();
if (texto.isEmpty()) return;
Tarefa t = new Tarefa(texto);
tarefas.add(t);
salvar(t); // persiste (paso 4)
campo.clearText();
renderizarLista(); // tú actualizas la UI manualmente
}
private void renderizarLista() {
listaBox.removeAll();
for (Tarefa t : tarefas) {
Button item = new Button(t.titulo);
item.onAction(c -> new DetalheScreen(t).show()); // navegación (paso 3)
listaBox.add(LAY_AUTO, item);
}
listaBox.setValidLayout(false); // re-mide el layout
Component.repaint(); // pide nuevo frame
}
3. Navegación push/pop
public class DetalheScreen extends Window {
private final Tarefa tarefa;
public DetalheScreen(Tarefa t) { this.tarefa = t; } // datos por el constructor
@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. Persistencia en SQLite (fuera de la 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(() -> { // vuelve al 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();
}
Dos reglas de oro de SQLite: el bind de parámetro es 1-based, los getters del cursor son 0-based; y la base de datos corre fuera de la paint thread, con el resultado volviendo vía
Component.onUI(...).
Lo que has construido
Una app que abre, añade, navega y persiste — y ya has visto los pilares que el
curso learn/ profundiza.
Próximos pasos
Ver también
- Layouts · Navegación · SQLite