对Java应用最常见的抱怨就是启动时间太长。这是因为Java虚拟机花费一段时间去加载所有必需的类,特别是对Swing应用,它们需要从Swing和AWT类库代码中去抽取大量的内容。
用户并不喜欢应用程序花费大量时间去产生初始屏幕,他们甚至可能不知道首次启动是否成功的情况下尝试着多次启动该应用程序。
此问题的解决之首是采用闪屏,即迅速出现的小窗体,它可以告诉用户该应用程序成功启动了。
传统上,这对于Java应用来说很难实现。当然,我们可以在main方法开始之后立即呈现一个窗体,但是,main方法只有在类加载器加载了所有需要依赖的类之后才会被启动,而这一过程可能要等上一段时间。
Java SE6支持虚拟机在启动时立即显示一幅图像而解决了这个问题。有两种机制可以指定这幅图像,一种使用命令行参数-splash:
java -splash:myImage.png package1.package2.MyApp
myImage.png文件需要在当前目录,否则要写全路径,相对或绝对
另一种是在JAR文件的清单中指定
Main-Class:package1.package2.MyApp
SplashScreen-Image:myimage.gif
这幅图像会在第一个AWT窗体可视时立即自动消失。我们可以使用任何GIF、JPEG或PNG图像,动画GIF和透明(GIF和PNG)都可以得到支持
如果应用程序在达到main之后即可立即执行,就不用考虑使用SplashScreen.
package swing.splash; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.SplashScreen; import java.awt.Toolkit; import java.util.List; import java.util.concurrent.TimeUnit; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; /*2015-7-7*/ public class SplashScreenTest { private static SplashScreen splash; private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEIGHT = 300; public static void main(String[] args) throws InterruptedException { init1(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { init2(); } }); } protected static void init2() { final Image img = Toolkit.getDefaultToolkit().getImage(splash.getImageURL()); final JFrame splashFrame = new JFrame(); splashFrame.setAlwaysOnTop(true); splashFrame.setUndecorated(true); final JPanel splashPanel = new JPanel() { private static final long serialVersionUID = 1L; @Override protected void paintComponent(Graphics g) { g.drawImage(img, 0, 0, null); } }; final JProgressBar progressBar = new JProgressBar(); progressBar.setStringPainted(true); splashPanel.setLayout(new BorderLayout()); splashPanel.add(progressBar, BorderLayout.SOUTH); splashFrame.add(splashPanel); splashFrame.setBounds(splash.getBounds()); splashFrame.setVisible(true); new SwingWorker<Void, Integer>() { @Override protected Void doInBackground() throws Exception { for (int i = 0; i < 100; i++) { publish(i); TimeUnit.SECONDS.sleep(1); } return null; } @Override protected void process(List<Integer> chunks) { for (Integer chunk : chunks) { progressBar.setString("Loading module" + chunk); progressBar.setValue(chunk); System.out.println(chunk); splashPanel.repaint();// because img is loaded asynchronously } } @Override protected void done() { splashFrame.setVisible(false); JFrame frame = new JFrame(); frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setTitle("SplashScreenTest"); frame.setVisible(true); frame.setLocationRelativeTo(null); } }; } private static void drawOnSplash(int percent) { Rectangle bounds = splash.getBounds(); Graphics2D g = splash.createGraphics(); int height = 20; int x = 2; int y = bounds.height - height - 4; int width = bounds.width - 4; Color brightPurple = new Color(76, 36, 121); g.setColor(brightPurple); g.fillRect(x, y, width * percent / 100, height); splash.update(); } private static void init1() throws InterruptedException { splash = SplashScreen.getSplashScreen(); if (splash == null) { System.err.println("Did you specify a splash image with -splash or in the manifest"); System.exit(1); } for (int i = 0; i < 100; i++) { drawOnSplash(i); System.out.println("init1:"+i); TimeUnit.SECONDS.sleep(1); } } }
Tips:
SplashScreen是Singleton,因此不能创建自己的SplashScreen对象。如果在命令行或清单(MANIFEST.MF)中没有设置任何SplashScreen,getSplashScreen方法将返回null.
时间: 2024-10-28 16:21:48