啟動畫面 (Splash Screen) 是現代應用程式的標準配備,既能展示品牌形象,又能在漫長的初始化過程中提供視覺回饋。本文循序漸進示範如何使用 Swing JWindow 建立專業級啟動畫面,涵蓋置中顯示、滑鼠點擊關閉、定時自動關閉、以及 Swing 執行緒安全處理。
class SplashWindow1 extends JWindow {
public SplashWindow1(String filename, Frame f) {
super(f);
JLabel l = new JLabel(new ImageIcon(filename));
getContentPane().add(l, BorderLayout.CENTER);
pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension labelSize = l.getPreferredSize();
setLocation(screenSize.width/2 - labelSize.width/2,
screenSize.height/2 - labelSize.height/2);
setVisible(true);
}
}
關鍵點:JWindow 無標題列/邊框/按鈕,適合啟動畫面;pack() 自動依圖片大小調整視窗;螢幕置中計算公式。
class SplashWindow2 extends JWindow {
public SplashWindow2(String filename, Frame f) {
super(f);
JLabel l = new JLabel(new ImageIcon(filename));
getContentPane().add(l, BorderLayout.CENTER);
pack();
// ... 置中程式碼同前 ...
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
setVisible(false);
dispose();
}
});
setVisible(true);
}
}
匿名 MouseAdapter 處理點擊事件,呼叫 dispose() 釋放原生資源。
class SplashWindow3 extends JWindow {
public SplashWindow3(String filename, Frame f, int waitTime) {
super(f);
JLabel l = new JLabel(new ImageIcon(filename));
getContentPane().add(l, BorderLayout.CENTER);
pack();
// ... 置中程式碼同前 ...
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
setVisible(false);
dispose();
}
});
final int pause = waitTime;
final Runnable closerRunner = new Runnable() {
public void run() {
setVisible(false);
dispose();
}
};
Runnable waitRunner = new Runnable() {
public void run() {
try {
Thread.sleep(pause);
SwingUtilities.invokeAndWait(closerRunner);
} catch (Exception e) {
e.printStackTrace();
}
}
};
setVisible(true);
Thread splashThread = new Thread(waitRunner, "SplashThread");
splashThread.start();
}
}
Swing 非執行緒安全,所有 UI 操作必須在 事件派發執行緒 (Event Dispatch Thread, EDT) 執行。waitRunner 運行於獨立執行緒,睡眠結束後不能直接呼叫 setVisible(false),必須透過 SwingUtilities.invokeAndWait() 將 closerRunner 排入 EDT 隊列執行。
| Runnable | 執行緒 | 職責 |
|---|---|---|
| waitRunner | 背景執行緒 (SplashThread) | 計時等待,不操作 UI |
| closerRunner | EDT (透過 invokeAndWait) | 實際關閉視窗、釋放資源 |
invokeAndWait:同步阻塞,等待 EDT 執行完畢才返回,適合「必須確保關閉完成」的場景invokeLater:非同步,立即返回,適合一般 UI 更新closerRunner,由主程式初始化完成後主動呼叫 setVisible(false); dispose()JWindow 加入 JProgressBar,主程式透過 SwingUtilities.invokeLater() 更新進度java.awt.SplashScreen 類別提供原生級啟動畫面支援 (需配合 -splash:image.png JVM 參數)public class MyApp {
public static void main(String[] args) {
// 1. 顯示啟動畫面 (4 秒自動關閉,可點擊關閉)
SplashWindow3 splash = new SplashWindow3("splash.png", null, 4000);
// 2. 執行耗時初始化 (資料庫連線、配置載入、快取預熱...)
try { Thread.sleep(3000); } catch (Exception e) {}
// 3. 啟動主視窗
SwingUtilities.invokeLater(() -> {
JFrame mainFrame = new JFrame("主程式");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(800, 600);
mainFrame.setVisible(true);
});
}
}
JWindow 而非 JFrame 避免多餘窗口元素SwingUtilities.invokeAndWait/InvokeLaterdispose() 必須呼叫以釋放原生視窗資源SplashScreen API