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();
}