在 Java 桌面應用開發中,修改視窗標題欄圖標與滑鼠游標是提升產品專業度的細節關鍵。`java.awt.Toolkit` 作為 AWT 的核心橋樑,提供了平台無關的方式獲取系統資源並實現這些客製化。本文將詳細演示如何正確設定應用程式圖標、任務列圖標、以及自訂滑鼠游標,並解決常見的圖標不顯示、游標熱點偏移、高 DPI 模糊等坑點。
`Toolkit` 是抽象類,`getDefaultToolkit()` 返回具體平台實現 (Windows: `SunToolkit`, Linux: `XToolkit` 等)。它封裝了:
public class MyFrame extends JFrame {
public MyFrame() {
setTitle("我的專業應用");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
// 1. 獲取 Toolkit
Toolkit tk = Toolkit.getDefaultToolkit();
// 2. 載入圖片 (建議使用 ClassLoader 獲取資源流,支援 JAR 內部讀取)
// 圖片建議: 16x16 (標題欄), 32x32 (任務列), 48x48/64x64 (Dock/高DPI)
// 格式: PNG (支援透明), ICO (Windows 多解析度容器)
Image icon16 = tk.createImage(getClass().getResource("/icons/app-16.png"));
Image icon32 = tk.createImage(getClass().getResource("/icons/app-32.png"));
Image icon64 = tk.createImage(getClass().getResource("/icons/app-64.png")); // 高 DPI
// 3. 設定圖標列表 (Java 6+ 支援多解析度列表,系統自動選最合適)
setIconImages(List.of(icon16, icon32, icon64));
// 兼容舊版單圖標 API (內部會取列表第一個)
// setIconImage(icon16);
setLocationRelativeTo(null); // 置中
setVisible(true);
}
}
Windows `.ico` 格式內部可包含多種解析度 (16, 32, 48, 256) 及色深。**最省事、相容性最好**的方式是直接載入一個精心製作的 `.ico`:
// 僅需一行,Windows 會自動從 ICO 選取適合標題欄/任務列/高DPI 的幀
Image icon = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/icons/app.ico"));
setIconImage(icon); // 或 setIconImages(List.of(icon));
public class CustomCursorDemo extends JFrame {
public CustomCursorDemo() {
setTitle("自訂游標演示");
setSize(600, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
Toolkit tk = Toolkit.getDefaultToolkit();
// 1. 載入游標圖片 (建議 32x32 或 64x64 PNG,透明背景)
// 熱點: 游標「生效點」相對圖片左上角的座標
// 例如: 箭頭尖端 (0,0)、十字中心 (16,16)、手掌點擊點 (5,5)
Image cursorImg = tk.createImage(getClass().getResource("/cursors/crosshair-32.png"));
// 2. 建立自訂游標
// 熱點設為圖片中心 (16,16) 適合十字準星
Point hotSpot = new Point(16, 16);
Cursor crosshairCursor = tk.createCustomCursor(cursorImg, hotSpot, "Crosshair");
// 3. 套用到元件 (整個視窗或特定面板)
JPanel panel = new JPanel() {
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("將滑鼠移入此區域 → 變十字準星", 50, 50);
}
};
panel.setCursor(crosshairCursor); // 關鍵:設在具體 Component 上
add(panel);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(CustomCursorDemo::new);
}
}
Toolkit 也提供標準游標,覆蓋 90% 場景:
Component comp = ...; // 任意元件 comp.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); // 箭頭 comp.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); // 十字 comp.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // 手掌 (超連結) comp.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); // 移動 (四向箭頭) comp.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); // I型 (文字編輯) comp.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); // 等待 (沙漏/轉圈) comp.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); // 北向調整 // ... S_RESIZE, E_RESIZE, W_RESIZE, NE_RESIZE, NW_RESIZE, SE_RESIZE, SW_RESIZE comp.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); // 西北調整
在高 DPI 螢幕上,標準 16x16 圖標會被系統放大變模糊。Java 9+ 支援 **多解析度圖片 (`MultiResolutionImage`)** 完美解決:
// Java 9+ 標準 API
import java.awt.image.MultiResolutionImage;
import java.util.List;
public class HiDPIIcon implements MultiResolutionImage {
private final List variants; // 16x, 32x, 48x, 64x, 128x...
public HiDPIIcon(List variants) { this.variants = variants; }
@Override public Image getResolutionVariant(double destWidth, double destHeight) {
// 系統詢問需要多大解析度,返回最接近且不小於目標的變體
return variants.stream()
.filter(img -> img.getWidth(null) >= destWidth && img.getHeight(null) >= destHeight)
.min(Comparator.comparingInt(img -> img.getWidth(null) * img.getHeight(null)))
.orElse(variants.get(variants.size()-1)); // 兜底返回最大
}
@Override public List getResolutionVariants() { return variants; }
@Override public int getWidth(ImageObserver o) { return variants.get(0).getWidth(o); }
@Override public int getHeight(ImageObserver o) { return variants.get(0).getHeight(o); }
}
// 使用
List icons = List.of(
tk.createImage(getClass().getResource("/icons/app-16.png")),
tk.createImage(getClass().getResource("/icons/app-32.png")),
tk.createImage(getClass().getResource("/icons/app-64.png")),
tk.createImage(getClass().getResource("/icons/app-128.png"))
);
frame.setIconImage(new HiDPIIcon(icons)); // 單一呼叫,自動適配所有 DPI
| 任務 | 推薦做法 | 關鍵 API |
|---|---|---|
| 視窗/任務列圖標 (Windows/Linux) | 單一 `.ico` (含多解析度) 或 `setIconImages(List<16,32,64>)` | `JFrame.setIconImage(s)`, `Toolkit.createImage` |
| macOS Dock 圖標 | 打包工具設定 `Info.plist` + `.icns` 檔案 | `jpackage`, `AppBundler`, `com.apple.eawt.Application` |
| 自訂游標 (圖片) | 32x32/64x64 PNG + 正確熱點 + `createCustomCursor` | `Toolkit.createCustomCursor`, `Component.setCursor` |
| 標準游標 (手型、等待、文字...) | 直接用預定義常數,無需圖片 | `Cursor.getPredefinedCursor` |
| 高 DPI 圖標適配 | 實作 `MultiResolutionImage` (Java 9+) | `MultiResolutionImage`, `setIconImage` |
| 資源載入 | 類路徑 `getClass().getResource()`,異步 `createImage` | `Class.getResource`, `Toolkit.createImage` |
掌握這些細節,您的 Java 桌面應用將擁有媲美原生應用的專業視覺體驗。記住:**圖標與游標是用戶感知品牌與交互狀態的第一觸點,值得投入精力打磨。**