如何实现实时更新JFrame中TextAutoCompleter的候选数据

本文介绍在java swing应用中,当外部文件(如.txt或.procesador)动态增删时,如何让textautocompleter自动刷新建议列表,避免重启窗口才能生效的问题。核心在于避免一次性初始化,改用按需/事件驱动方式重载数据源。

在您当前的代码中,procesadorlistado 数组仅在 Registrar 构造方法中初始化一次:

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

因此,即使用户新增或删除 .procesador 文件,AutocompletarProcesador 始终持有旧快照,导致自动完成项“滞后”。

✅ 正确做法:按需重载 + 生命周期感知

推荐两种稳定、低侵入的解决方案(任选其一):

方案一:每次获得焦点时刷新(推荐,轻量且用户友好)

在文本框 TProcesador1 获取焦点时重新扫描目录并重建自动完成器:

private void setupAutoCompleterOnFocus() {
    TProcesador1.addFocusListener(new FocusAdapter() {
        @Override
        public void focusGained(FocusEvent e) {
            refreshAutoCompleter();
        }
    });
}

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

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

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

    // ✅ 安全重建:先清空,再添加新项
    AutocompletarProcesador.removeAllItems();
    for (File f : archivos) {
        String itemName = f.getName().replace(".procesador", "");
        AutocompletarProcesador.addItem(itemName);
    }
}

并在 initComponents() 后调用:

public Registrar() {
    initComponents();
    setLocationRelativeTo(this);
    setupAutoCompleterOnFocus(); // ✅ 注册焦点监听
    refreshAutoCompleter();      // ✅ 首次加载(替代原 AutocompleterReg)
}
? 优势:无需定时轮询,无性能开销;用户每次开始输入前已确保数据最新;完全兼容方向键选择、回车确认等交互逻辑。

方案二:使用 WatchService 实现真正的实时监听(进阶)

若需毫秒级响应(如后台服务持续写入),可启用 Java NIO 的文件系统监听:

private WatchService watchService;
private void startDirectoryWatcher() {
    try {
        watchService = FileSystems.getDefault().newWatchService();
        Path path = Paths.get(System.getProperty("user.dir"), "Procesador");
 

path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); // 启动监听线程(注意:应在非EDT线程中运行) new Thread(() -> { while (true) { WatchKey key; try { key = watchService.take(); // 阻塞等待事件 } catch (InterruptedException e) { break; } for (WatchEvent event : key.pollEvents()) { if (event.context() instanceof Path p && p.toString().toLowerCase().endsWith(".procesador")) { SwingUtilities.invokeLater(this::refreshAutoCompleter); // 切回EDT更新UI } } key.reset(); } }, "ProcesadorWatcher").start(); } catch (IOException e) { e.printStackTrace(); } }

⚠️ 注意事项:

  • TextAutoCompleter(常见于 org.jdesktop.swingx.autocomplete)不支持动态修改已有实例的内部列表,必须调用 removeAllItems() + addItem() 或新建实例;
  • 切勿在 Timer 中高频调用 refreshAutoCompleter() —— 这会干扰用户键盘导航(如方向键失效),因频繁重建破坏组件内部状态;
  • 确保 File.listFiles() 返回非 null,并过滤空/非法文件(.procesador 扩展名校验);
  • 若项目使用 Maven,确认依赖为 swingx-autocomplete(如 org.swinglabs.swingx:swingx-all:1.6.5-1)。

✅ 总结:不要“静态缓存文件列表”,而要“动态获取+安全重建”。方案一足以覆盖绝大多数桌面应用场景,简洁、可靠、零副作用。