如何实现实时更新JFrame中AutoComplete组件的数据源

本文介绍在java swing中解决autocomplete组件(如textautocompleter)无法动态响应文件系统变化的问题,核心是避免一次性静态加载,改用按需刷新机制,确保新增/删除.txt或.procesador文件后无需重启jframe即可实时生效。

在您的代码中,procesadorlistado 数组仅在类初始化阶段(即 Registrar 构造方法执行时)读取一次磁盘文件列表:

File contenedorpro = new File(ubicacionpro);
File[] procesadorlistado = contenedorpro.listFiles(); // ❌ 仅执行一次,后续变更不感知

这导致 AutocompletarProcesador 始终基于启动时的快照数据工作——即使您后续向 Procesador/ 目录添加或删除 .procesador 文

件,自动完成下拉列表也不会更新,除非强制重启整个 JFrame。

✅ 正确做法:按需动态刷新数据源

应将文件扫描逻辑封装为独立方法,并在关键时机(如窗口获得焦点、输入框获得焦点、或用户主动触发刷新)重新加载并重置 AutoCompleter。推荐以下两种稳健方案:

方案一:监听输入框焦点事件(推荐)

在用户开始输入前确保数据最新,兼顾性能与实时性:

private void setupAutoCompleter() {
    AutocompletarProcesador = new TextAutoCompleter(TProcesador1);
    refreshAutoCompleterItems(); // 首次初始化

    // 每次输入框获得焦点时刷新数据(确保最新)
    TProcesador1.addFocusListener(new FocusAdapter() {
        @Override
        public void focusGained(FocusEvent e) {
            refreshAutoCompleterItems();
        }
    });
}

private void refreshAutoCompleterItems() {
    String barrapro = File.separator;
    String ubicacionpro = System.getProperty("user.dir") + barrapro + "Procesador" + barrapro;
    File contenedorpro = new File(ubicacionpro);

    if (!contenedorpro.exists() || !contenedorpro.isDirectory()) {
        return;
    }

    File[] files = contenedorpro.listFiles((dir, name) -> name.toLowerCase().endsWith(".procesador"));
    if (files == null) return;

    // 清空旧项,重新填充(TextAutoCompleter 通常支持 clear() 或需重建)
    AutocompletarProcesador.removeAllItems(); // 若支持;否则需重建实例
    for (File f : files) {
        String itemName = f.getName().replace(".procesador", "");
        AutocompletarProcesador.addItem(itemName);
    }
}
⚠️ 注意:TextAutoCompleter 的 removeAllItems() 方法是否可用取决于具体实现版本。若不可用,请改用重建方式:AutocompletarProcesador = new TextAutoCompleter(TProcesador1); // 重建新实例 // ... 再 addItem

方案二:使用 WatchService 实现真正的文件系统监听(进阶)

适用于高频变更场景,避免轮询开销:

private WatchService watchService;
private void startFileWatcher() {
    try {
        Path procesadorPath = Paths.get(System.getProperty("user.dir"), "Procesador");
        watchService = FileSystems.getDefault().newWatchService();
        procesadorPath.register(watchService,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_DELETE,
            StandardWatchEventKinds.ENTRY_MODIFY);

        // 启动监听线程(注意:需在后台线程中运行,避免阻塞UI)
        new Thread(() -> {
            while (true) {
                WatchKey key;
                try {
                    key = watchService.take(); // 阻塞等待事件
                } catch (InterruptedException e) { break; }
                for (WatchEvent event : key.pollEvents()) {
                    // 触发刷新(务必通过 SwingUtilities.invokeLater 安全更新UI)
                    SwingUtilities.invokeLater(this::refreshAutoCompleterItems);
                }
                key.reset();
            }
        }, "ProcesadorWatcher").start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

调用 startFileWatcher() 即可在构造方法末尾启用——此后任何 .procesador 文件增删都会自动触发下拉列表更新。

? 总结建议

  • 避免静态缓存:不要将 File[] 声明为类成员变量并只初始化一次;
  • 优先使用焦点刷新:简单可靠,用户体验无感知;
  • 慎用 Timer 轮询:易干扰键盘导航(如方向键选择),且增加CPU负担;
  • 确保线程安全:所有 UI 更新必须在 Event Dispatch Thread 中执行(SwingUtilities.invokeLater);
  • 增强健壮性:添加 null 检查、目录存在性判断及异常日志,防止因文件权限或路径错误导致崩溃。

通过上述任一方案,您的 TProcesador1 输入框即可真正实现“Google式”智能提示——所见即所得,实时反映文件系统最新状态。