Java 带有进度条的Swing启动屏幕

Java 带有进度条的Swing启动屏幕,java,swing,splash-screen,jprogressbar,invokelater,Java,Swing,Splash Screen,Jprogressbar,Invokelater,这是我的闪屏代码 public class SplashScreen extends JWindow { private static final long serialVersionUID = 1L; private BorderLayout borderLayout = new BorderLayout(); private JLabel imageLabel = new JLabel(); private JProgressBar progressBar = new JProgressB

这是我的闪屏代码

public class SplashScreen extends JWindow {

private static final long serialVersionUID = 1L;
private BorderLayout borderLayout = new BorderLayout();
private JLabel imageLabel = new JLabel();
private JProgressBar progressBar = new JProgressBar(0, 100);

public SplashScreen(ImageIcon imageIcon) {
    imageLabel.setIcon(imageIcon);
    setLayout(borderLayout);
    add(imageLabel, BorderLayout.CENTER);
    add(progressBar, BorderLayout.SOUTH);
    pack();
    setLocationRelativeTo(null);
}

public void showScreen() {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            setVisible(true);
        }
    });
}

public void close() {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            setVisible(false);
            dispose();
        }
    });
}

public void setProgress(final String message, final int progress) {
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            progressBar.setValue(progress);
            if (message == null) {
                progressBar.setStringPainted(false);
            } else {
                progressBar.setStringPainted(true);
            }
            progressBar.setString("Loading " + message + "...");
        }
    });
}
}
从我调用的主要方法来看

public static void main(String[] args) {

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            try {
                UIManager.setLookAndFeel(UIManager
                        .getSystemLookAndFeelClassName());

                SplashScreen splashScreen = new SplashScreen(new ImageIcon("images/splash.jpg"));
                splashScreen.showScreen();

                AppFrame frame = new AppFrame(splashScreen);

            } catch (Exception e) {
                appLogger.error(e.getMessage(), e); 
            }
        }
    });
}
在我调用的AppFrame的构造函数中, splashScreen.setProgress(msg,val) 方法更新进度条。但是水花没有显现出来。即使加载需要很长的时间,当帧仅显示一小部分秒时,它仅在末尾显示。但如果我把这三行

SplashScreen splashScreen = new SplashScreen(new ImageIcon("images/splash.jpg"));
splashScreen.showScreen();
AppFrame frame = new AppFrame(splashScreen);
在invokeLater()外部,会显示启动屏幕,进度条会很好地更新。我相信GUI更新应该在invokeLater中。有什么问题吗

顺便说一句,AppFrame加载我的应用程序的各种面板

编辑: 下面显示了我的AppFrame的模拟

public class AppFrame extends JFrame {

public AppFrame(SplashScreen splashScreen) {
    JPanel test = new JPanel();
    test.setLayout(new GridLayout(0, 10));

    splashScreen.setProgress("jlabel", 10);
    for(int i = 0; i < 10000; i++) {
        test.add(new JButton("Hi..." + i));
        splashScreen.setProgress("jbutton", (int)(i * 0.1));
    }
    add(new JScrollPane(test));
    setPreferredSize(new Dimension(800, 600));
    pack();
    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    setLocationRelativeTo(null);
    splashScreen.setProgress("complete", 100);
    setVisible(true);
}
}
公共类AppFrame扩展了JFrame{
公共应用程序框架(SplashScreen SplashScreen){
JPanel测试=新的JPanel();
test.setLayout(新的GridLayout(0,10));
splashScreen.setProgress(“jlabel”,10);
对于(int i=0;i<10000;i++){
添加(新的JButton(“Hi…”+i));
设置进度(“jbutton”(int)(i*0.1));
}
添加(新JScrollPane(测试));
设置首选尺寸(新尺寸(800600));
包装();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setLocationRelativeTo(空);
splashScreen.setProgress(“完成”,100);
setVisible(真);
}
}

Hmm看看我用
SplashScreen
类制作的这个工作示例(只使用一个简单的
计时器和
ActionListener
来增加
进度条的值,直到显示100帧):


一个
invokedLater
从另一个
invokeLater
调用。只有在第一个代码的执行完成时,才会执行
runnable

您应该修改Splashscreen代码,如下所示:

...
private void runInEdt(final Runnable runnable) {
    if (SwingUtilities.isEventDispatchThread())
        runnable.run();
    else
        SwingUtilities.invokeLater(runnable);
}

public void showScreen() {
    runInEdt(new Runnable() {
        public void run() {
            setVisible(true);
        }
    });
}

public void close() {
    runInEdt(new Runnable() {
        public void run() {
            setVisible(false);
            dispose();
        }
    });
}

public void setProgress(final String message, final int progress) {
    runInEdt(new Runnable() {
        public void run() {
            progressBar.setValue(progress);
            if (message == null)
                progressBar.setStringPainted(false);
            else
                progressBar.setStringPainted(true);
            progressBar.setString("Loading " + message + "...");
        }
    });
}

我认为这样您就可以保留启动屏幕了。

“我认为GUI更新应该在invokeLater中。”或者
invokeAndWait
。但是做一些日志记录,我希望您能找到
splashScreen.showScreen()完成之间的时间间隔和<代码>AppFrame frame=新的AppFrame(splashScreen)比您预期的要少得多。还要注意的是,最好的飞溅是严格的AWT(无摆动)。这意味着我应该使用窗口或框架而不是JWindow?谢谢。是的,请使用
窗口
框架
。也可以查看<代码> MediaTracker <代码> >代码>画布< /COD>和<代码>事件队列——不必使用Swing导入或类,或者整个Swing包被加载第一。真的是ui创建需要这么长时间吗?若并没有,提取实时HOG(!ui=!EDT)并在SwingWorker的doInBackground中准备它们,将中间进度发布到splash并关闭doneI中的splash/show应用程序。我尝试了您的解决方案。因为它已经在EDT中,所以“RUNINDT”中的else部分将不会执行。不管怎样,我还是有同样的问题。EDT中未更新进度条。我将其用作一个函数,并在main()中添加了invokeLater。如果没有invokeLater,它可以正常工作。“runInEDT”方法确保您的Splashscreen是线程安全的。它可以从任何线程调用。我不明白为什么我的解决方案不适合你。也许你可以提供更多关于AppFrame构造器内容的详细信息。请查看带有我的AppFrame模拟的编辑。基本上,我是在该构造函数中加载我的整个应用程序组件。所以我想在加载组件时更新进度条。但它并没有显示,即使它需要相当长的时间来加载。你确定这种模拟行为与真实行为是等效的吗?我的意思是,一般来说,加载时间不是由于gui初始化,而是由于一个团队成员的dao代码在EDT中运行。
...
private void runInEdt(final Runnable runnable) {
    if (SwingUtilities.isEventDispatchThread())
        runnable.run();
    else
        SwingUtilities.invokeLater(runnable);
}

public void showScreen() {
    runInEdt(new Runnable() {
        public void run() {
            setVisible(true);
        }
    });
}

public void close() {
    runInEdt(new Runnable() {
        public void run() {
            setVisible(false);
            dispose();
        }
    });
}

public void setProgress(final String message, final int progress) {
    runInEdt(new Runnable() {
        public void run() {
            progressBar.setValue(progress);
            if (message == null)
                progressBar.setStringPainted(false);
            else
                progressBar.setStringPainted(true);
            progressBar.setString("Loading " + message + "...");
        }
    });
}
    Thread l_splash_thread = new Thread(
            new SplashScreen ());
    l_splash_thread.start();

    try {
        l_splash_thread.join();
    } catch (InterruptedException e1) {
        e1.printStackTrace();
    }