java - 如何显示启动画面,在后台加载数据,然后隐藏启动画面?

标签 java multithreading swing splash-screen countdownlatch

我正在设计一个简单的 JavaFX 表单。

首先,我加载 JavaFX 环境(并等待其完成),如下所示:

final CountDownLatch latch_l = new CountDownLatch(1);
try {
    // init the JavaFX environment
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            new JFXPanel(); // init JavaFX
            latch_l.countDown();
        }
    });
    latch_l.await();
}

这很好用。 (之所以需要先这样加载JavaFX,是因为它主要是一个Swing应用程序,里面有一些JavaFX组件,但它们是稍后加载的)

现在,我想在启动时添加一个启动屏幕,并在 JavaFX 环境加载时显示它(实际上在屏幕上显示大约 5 秒钟,因为有 Logo 、商标等......我需要展示的应用程序)

所以我想出了一个 SplashScreen 类,它只在屏幕上显示一个 JWindow,如下所示:

public class SplashScreen {

    protected JWindow splashScreen_m = new JWindow();
    protected Integer splashScreenDuration_m = 5000;

    public void show() {
        // fill the splash-screen with informations
        ...

        // display the splash-screen
        splashScreen_m.validate();
        splashScreen_m.pack();
        splashScreen_m.setLocationRelativeTo(null);
        splashScreen_m.setVisible(true);
    }

    public void unload() {
        // unload the splash-screen
        splashScreen_m.setVisible(false);
        splashScreen_m.dispose();
    }
}

现在,我希望启动画面能够加载并自动显示 5 秒。 同时,我也希望加载 JavaFX 环境。

所以我像这样更新了 CountDownLatch :

final CountDownLatch latch_l = new CountDownLatch(2); // now countdown is set to 2

final SplashScreen splash_l = new SplashScreen();

try {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            // show splash-screen
            splash_l.show();
            latch_l.countDown();

            // init the JavaFX environment
            new JFXPanel(); // init JavaFX
            latch_l.countDown();
        }
    });
    latch_l.await();
    splash_l.unload();
}

所以,它正在工作,但是启动画面只停留在 JavaFX 环境加载时,所以基本上它卸载得非常快(考虑到我编写的代码,这是正常的)。

如何在不卡住 EDT 的情况下显示启动屏幕至少 5 秒(如果 JavaFX 加载速度更快)?

谢谢。

最佳答案

最重要的问题是您阻塞了事件调度线程,这意味着它在被阻塞时无法显示/更新任何内容。同样的问题也适用于 JavaFX。

您还应该永远不要从各自的事件队列之外的任何其他地方进行更新。

现在,您可以通过多种方式来实现此目的,但 SwingWorker 可能是目前最简单的。

enter image description here

抱歉,这就是我对 JavaFX 的全部接触...

public class TestJavaFXLoader extends JApplet {

    public static void main(String[] args) {
        new TestJavaFXLoader();
    }

    public TestJavaFXLoader() throws HeadlessException {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                Loader loader = new Loader();
                loader.addPropertyChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        if (evt.getPropertyName().equals("state") && evt.getNewValue().equals(SwingWorker.StateValue.DONE)) {
                            System.out.println("Load main app here :D");
                        }
                    }
                });
                loader.load();
            }
        });
    }

    public class Loader extends SwingWorker<Object, String> {

        private JWindow splash;
        private JLabel subMessage;

        public Loader() {
        }

        protected void loadSplashScreen() {
            try {
                splash = new JWindow();
                JLabel content = new JLabel(new ImageIcon(ImageIO.read(...))));
                content.setLayout(new GridBagLayout());
                splash.setContentPane(content);

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridwidth = GridBagConstraints.REMAINDER;

                subMessage = createLabel("");

                splash.add(createLabel("Loading, please wait"), gbc);
                splash.add(subMessage, gbc);
                splash.pack();
                splash.setLocationRelativeTo(null);
                splash.setVisible(true);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        protected JLabel createLabel(String msg) {
            JLabel message = new JLabel("Loading, please wait");
            message.setForeground(Color.CYAN);
            Font font = message.getFont();
            message.setFont(font.deriveFont(Font.BOLD, 24));
            return message;
        }

        public void load() {
            if (!EventQueue.isDispatchThread()) {
                try {
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            loadSplashScreen();
                        }
                    });
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            } else {
                loadSplashScreen();
            }
            execute();
        }

        @Override
        protected void done() {
            splash.dispose();
        }

        @Override
        protected void process(List<String> chunks) {
            subMessage.setText(chunks.get(chunks.size() - 1));
        }

        @Override
        protected Object doInBackground() throws Exception {

            publish("Preparing to load application");
            try {
                Thread.sleep(2500);
            } catch (InterruptedException interruptedException) {
            }
            publish("Loading JavaFX...");

            runAndWait(new Runnable() {
                @Override
                public void run() {
                    new JFXPanel();
                }
            });

            try {
                Thread.sleep(2500);
            } catch (InterruptedException interruptedException) {
            }
            return null;
        }

        public void runAndWait(final Runnable run)
                throws InterruptedException, ExecutionException {
            if (Platform.isFxApplicationThread()) {
                try {
                    run.run();
                } catch (Exception e) {
                    throw new ExecutionException(e);
                }
            } else {
                final Lock lock = new ReentrantLock();
                final Condition condition = lock.newCondition();
                lock.lock();
                try {
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            lock.lock();
                            try {
                                run.run();
                            } catch (Throwable e) {
                                e.printStackTrace();
                            } finally {
                                try {
                                    condition.signal();
                                } finally {
                                    lock.unlock();
                                }
                            }
                        }
                    });
                    condition.await();
//                    if (throwableWrapper.t != null) {
//                        throw new ExecutionException(throwableWrapper.t);
//                    }
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

我找到了runAndWait代码here

关于java - 如何显示启动画面,在后台加载数据,然后隐藏启动画面?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14928586/

相关文章:

Java 分割整数链表

java - new String(byte[]) 和 DatatypeConverter.printBase64Binary(byte[]) 有什么区别?

ruby 线程,杀死与终止与退出

c++ - Effective placement of lock_guard - 来自 Effective Modern C++ 的第 16 条

java - Java 中的事件序列

java - 为什么 JSP 页面编码指令应该在 JSP 中的第一行?

ios - UIViewController 元素(通过 IB 在 Main.storyboard 中创建)究竟何时启动/加载?

Java GUI 难倒了。 JFrame中只有一个显示

java - 如何去除jSlider的边框

java - 有没有办法在 JavaFX LineChart 中断开串联的 2 个点?