swing中的JavaFX文件选择器

swing中的JavaFX文件选择器,swing,javafx,Swing,Javafx,我想每个人都会同意JFileChooser真的很糟糕。所以我在寻找一个替代方案,发现JavaFX有一个很棒的FileChooser类。那么现在一个明显的问题是:如何将这个整洁的文件选择器嵌入到我的Swing应用程序中 不用说,在我发布这篇文章之前,我做了一些研究,这是我到目前为止发现的: JavaFXFileDialog类的代码非常有趣,但当我退出应用程序时它不会关闭(JavaFX似乎继续在后台运行)。此外,我还缺少一些可以传递给文件选择器的字段,如设置默认目录的路径。我不喜欢它是静态的 我非常

我想每个人都会同意JFileChooser真的很糟糕。所以我在寻找一个替代方案,发现JavaFX有一个很棒的FileChooser类。那么现在一个明显的问题是:如何将这个整洁的文件选择器嵌入到我的Swing应用程序中

不用说,在我发布这篇文章之前,我做了一些研究,这是我到目前为止发现的:

JavaFXFileDialog类的代码非常有趣,但当我退出应用程序时它不会关闭(JavaFX似乎继续在后台运行)。此外,我还缺少一些可以传递给文件选择器的字段,如设置默认目录的路径。我不喜欢它是静态的


我非常感谢您的帮助。

除了您提到的问题外,该对话框的代码还有多个问题。例如,它不能处理JavaFX平台在调用
isJavaFXStillAvailable()
之后立即关闭,但在调用
platform.runLater()
之前关闭的情况,这仍然会使它永远挂起。我也不喜欢那个巨大的
同步的
块,尽管它似乎没有任何真正的问题。我也不明白为什么“愚蠢的同步对象必须是一个字段”——每次调用
chooseFileWithJavaFXDialog()
都是相互独立的,所以它也可以使用本地最终锁(即使是该数组也可以)

使JVM正确退出的正确方法是在关闭应用程序时调用
Platform.exit()
(可能在主窗口的windowClosed()中)。您需要手动执行此操作,因为chooser类不知道您是否还需要JavaFX,并且一旦关闭它就无法重新启动它

这段代码启发我开发了一个实用程序类,用于调用JavaFX事件线程中的任何代码,并将结果返回给调用线程,很好地处理各种异常和JavaFX状态:

/**
 * A utility class to execute a Callable synchronously
 * on the JavaFX event thread.
 * 
 * @param <T> the return type of the callable
 */
public class SynchronousJFXCaller<T> {
    private final Callable<T> callable;

    /**
     * Constructs a new caller that will execute the provided callable.
     * 
     * The callable is accessed from the JavaFX event thread, so it should either
     * be immutable or at least its state shouldn't be changed randomly while
     * the call() method is in progress.
     * 
     * @param callable the action to execute on the JFX event thread
     */
    public SynchronousJFXCaller(Callable<T> callable) {
        this.callable = callable;
    }

    /**
     * Executes the Callable.
     * <p>
     * A specialized task is run using Platform.runLater(). The calling thread
     * then waits first for the task to start, then for it to return a result.
     * Any exception thrown by the Callable will be rethrown in the calling
     * thread.
     * </p>
     * @param startTimeout time to wait for Platform.runLater() to <em>start</em>
     * the dialog-showing task
     * @param startTimeoutUnit the time unit of the startTimeout argument
     * @return whatever the Callable returns
     * @throws IllegalStateException if Platform.runLater() fails to start
     * the task within the given timeout
     * @throws InterruptedException if the calling (this) thread is interrupted
     * while waiting for the task to start or to get its result (note that the
     * task will still run anyway and its result will be ignored)
     */
    public T call(long startTimeout, TimeUnit startTimeoutUnit)
            throws Exception {
        final CountDownLatch taskStarted = new CountDownLatch(1);
        // Can't use volatile boolean here because only finals can be accessed
        // from closures like the lambda expression below.
        final AtomicBoolean taskCancelled = new AtomicBoolean(false);
        // a trick to emulate modality:
        final JDialog modalBlocker = new JDialog();
        modalBlocker.setModal(true);
        modalBlocker.setUndecorated(true);
        modalBlocker.setOpacity(0.0f);
        modalBlocker.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        final CountDownLatch modalityLatch = new CountDownLatch(1);
        final FutureTask<T> task = new FutureTask<T>(() -> {
            synchronized (taskStarted) {
                if (taskCancelled.get()) {
                    return null;
                } else {
                    taskStarted.countDown();
                }
            }
            try {
                return callable.call();
            } finally {
                // Wait until the Swing thread is blocked in setVisible():
                modalityLatch.await();
                // and unblock it:
                SwingUtilities.invokeLater(() ->
                        modalBlocker.setVisible(false));
            }
        });
        Platform.runLater(task);
        if (!taskStarted.await(startTimeout, startTimeoutUnit)) {
            synchronized (taskStarted) {
                // the last chance, it could have been started just now
                if (!taskStarted.await(0, TimeUnit.MILLISECONDS)) {
                    // Can't use task.cancel() here because it would
                    // interrupt the JavaFX thread, which we don't own.
                    taskCancelled.set(true);
                    throw new IllegalStateException("JavaFX was shut down"
                            + " or is unresponsive");
                }
            }
        }
        // a trick to notify the task AFTER we have been blocked
        // in setVisible()
        SwingUtilities.invokeLater(() -> {
            // notify that we are ready to get the result:
            modalityLatch.countDown();
        });
        modalBlocker.setVisible(true); // blocks
        modalBlocker.dispose(); // release resources
        try {
            return task.get();
        } catch (ExecutionException ex) {
            Throwable ec = ex.getCause();
            if (ec instanceof Exception) {
                throw (Exception) ec;
            } else if (ec instanceof Error) {
                throw (Error) ec;
            } else {
                throw new AssertionError("Unexpected exception type", ec);
            }
        }
    }

}
使用此类,您可以在factory方法中初始化选择器,或者,如果需要对每个调用执行不同的初始化,您可以将自定义方法传递给
showDialog()

    System.out.println(chooser.showDialog(ch -> {
        ch.setInitialDirectory(new File(System.getProperty("user.home")));
        return ch.showOpenDialog(null);
    }));

首先,非常感谢您的努力!目前我已经没有Java知识了,因为我们在大学里不得不改用C语言,而且我很快就要期末考试了。所以,如果你能给我一个月的时间,这样我就可以熟悉我的代码并尝试实现你的建议。我会尽快向你汇报并记下你的答案!我必须承认,我正在努力找出如何正确实施这一点。您能否给我一个详细的例子,说明如何在一个非常简单的swing应用程序(可能是一个带有两个按钮的JFrame)中使用它?如果能够指定当前目录、当前文件名和扩展名过滤器,那就太好了。再次感谢您并为需要这么多帮助表示歉意:|@Haeri,我不明白。我在上一段代码中给出了一个如何使用它的示例,可以从任何Swing应用程序安全地调用它。只需将其插入您可以想象的任何代码,而不是像我那样插入println()调用。在我的示例中,我已经指定了一个current dir,您实际上可以在lambda表达式中调用
ch
上的任何方法,指定过滤器或您需要的任何其他方法。
    System.out.println(chooser.showDialog(ch -> {
        ch.setInitialDirectory(new File(System.getProperty("user.home")));
        return ch.showOpenDialog(null);
    }));