本页面由葡萄牙语机器翻译,可能存在错误。葡萄牙语版本为准。
你的第一个应用:任务清单
我们将构建一个能打开、添加、导航并持久化的应用——一步一步,按需引入每一个 组成部分。
完整示例(复制、粘贴、运行)
一个精简、自包含的核心版本:一个 TextField + Button,向虚拟列表添加项目。将其
粘贴到一个 MainWindow 类中,并用 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());
// 输入行:字段(吸收宽度)+ 按钮(自然大小)
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);
// 虚拟列表:每个单元格为字体高度的 3 倍
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,再 index
{
((Label) view).setText(tarefas.get(index)); // 直接强制转换
}
});
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); // 始终 2 个参数
return;
}
tarefas.add(texto);
campo.clearText();
lista.notifyDataChanged(); // 重新测量并重绘列表
}
}
下面的片段只展示核心内容(放进
onShow()里的部分)。要运行的话,请把它们放入上面的MainWindow骨架中。
1. 界面骨架(VerticalLayout)
public class TarefasScreen extends Window {
private final List<Tarefa> tarefas = new ArrayList<>(); // 状态保存在 Window 的字段中
private Component listaBox;
private TextField campo;
@Override public void onShow() throws Exception {
super.onShow();
setLayout(new VerticalLayout());
// 输入行(字段 + 按钮)
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);
// 列表区域,吸收剩余高度
listaBox = new Component();
listaBox.setLayout(new VerticalLayout());
add(LAY_FILL, listaBox);
add.onAction(c -> adicionar());
carregar(); // 从数据库填充
}
}
2. 添加并反映到 UI(无响应式绑定)
private void adicionar() {
String texto = campo.getText().trim();
if (texto.isEmpty()) return;
Tarefa t = new Tarefa(texto);
tarefas.add(t);
salvar(t); // 持久化(步骤 4)
campo.clearText();
renderizarLista(); // 你手动更新 UI
}
private void renderizarLista() {
listaBox.removeAll();
for (Tarefa t : tarefas) {
Button item = new Button(t.titulo);
item.onAction(c -> new DetalheScreen(t).show()); // 导航(步骤 3)
listaBox.add(LAY_AUTO, item);
}
listaBox.setValidLayout(false); // 重新测量布局
Component.repaint(); // 请求新的一帧
}
3. push/pop 导航
public class DetalheScreen extends Window {
private final Tarefa tarefa;
public DetalheScreen(Tarefa t) { this.tarefa = t; } // 通过构造函数传入数据
@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. SQLite 持久化(在 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()) // getter 从 0 开始
carregadas.add(new Tarefa(c.getInt(0), c.getString(1)));
}
} catch (Exception e) { Debug.handle(e); }
Component.onUI(() -> { // 返回 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 开始
} catch (Exception e) { Debug.handle(e); }
}).start();
}
SQLite 的两条黄金规则: 参数绑定是 1-based,游标的 getter 是 0-based;并且数据库运行在 paint thread 之外,结果通过
Component.onUI(...)返回。
你构建了什么
一个能打开、添加、导航并持久化的应用——你也已经见到了
learn/ 课程将深入讲解的那些支柱。
下一步
→ 项目剖析
另请参阅
这个页面有帮助吗?感谢你的反馈!