Java 憑藉跨平台、物件導向、豐富 API 稱霸中間層,但在桌面領域長期因「效能不如原生 C/C++」而難以突圍。對於複雜 UI(如多媒體播放器、儀表板),Swing/AWT 的矩形視窗限制與繪圖效能成為瓶頸。本文展示一條創新路徑:JNI + SLIK (Simplistic Skin Interface),讓 Java 代碼調用跨平台 C 皮膚引擎,實現「跨平台編譯、原生執行效能、非矩形視窗」三大目標。
// 頂層容器
GtkSkin // 封裝帶皮膚視窗:載入/卸載/最小化/圖標化、事件監聽
GtkSkinNatives // JNI 本地方法介面:繪製視窗、處理原生事件
關鍵特性:幾何形狀與所有子元件基於高解析度圖片(規格檔 skindata 定義),支援透明度、非矩形區域。
WButton、WSlider、WDial(撥號盤)、WMenu、WList(圖形化播放列表)、WLabel、WTextskindata 讀取[main]
image = main-complete.png
transparent = FALSE
border = TRUE
border_left = 116
border_right = 63
[button_exit]
image = btn-12-exit.png
x = 252
y = 2
prelight = TRUE
完整規格請參考 SKIN-SPECS 文件。
public final class SkinDemo implements
WindowEventListener, // 視窗關閉/滑鼠拖曳/點擊
ButtonEventListener, // 按鈕點擊
ListEventListener, // 列表行點擊
DialEventListener, // 撥號盤拖曳
SliderEventListener, // 滑桿拖曳
MenuEventListener // 選單選擇
{
// ...
}
public SkinDemo(String[] args) throws GtkSkinException {
// 初始化 GTK 運行時 + JNI 偵錯旗標
skin = new GtkSkin("My App", GtkSkin.GTK_TRUE);
// 註冊所有規格檔定義的元件
skin.initAllWidgets();
skin.registerWidget(new WText("title"), "Title goes here!");
skin.registerWidget(new WButton("exit"));
skin.registerWidget(new WButton("iconify"));
// ...
// 監聽視窗層級事件
skin.addWindowEventListener(this);
// 載入規格檔並顯示
skin.loadSkin(skinPath);
skin.show();
}
// 撥號盤
position = new WDial("position");
position.addDialListener(this);
skin.registerWidget(position);
// 滑桿
volume = new WSlider("volume");
volume.addSliderListener(this);
skin.registerWidget(volume);
// 彈出選單
String[] menus = {
"Menu 1" + WMenu.SUBMENU_DIVIDER + "Submenu-11" + WMenu.SUBMENU_DIVIDER + "Submenu-12",
"Menu2" + WMenu.SUBMENU_DIVIDER + "Submenu21" + WMenu.MENU_DIVIDER + WMenu.SUBMENU_DIVIDER + "SubM31",
WMenu.MENU_DIVIDER, "Menu3", "Menu4"
};
popup = new WMenu("popup", menus, skinHandle);
popup.addMenuListener(this);
skin.registerWidget(popup, menus);
static {
System.loadLibrary("slikjni"); // Windows: slikjni.dll, Linux: libslikjni.so
}
public class GtkSkinNatives {
private native long createSkin(String title, boolean debug);
private native void loadSkin(long handle, String path);
private native void showSkin(long handle);
private native void registerWidget(long skinHandle, String widgetType, String widgetKey);
// ... 其他原生操作
}
SLIK 產生的 GTK 事件透過 JNI 級聯回調至 Java 監聽器:
// C 代碼片段 JNIEXPORT void JNICALL Java_jni_skin_slik_GtkSkinNatives_fireButtonEvent (JNIEnv *env, jobject obj, jlong skinHandle, jstring widgetKey, jint eventType) { // 查找 Java 監聽器並呼叫 buttonClicked(widgetKey) }
繼承 java.awt.Component 或 Container,實作 ViewAwareComponent 介面,在 HTML 主題檔用 <object CLASSID="java:YourClass"> 嵌入。
public class LightWeightCom extends JLabel implements ViewAwareComponent {
private View myView;
private HTMLDocument doc;
private URL base;
private HelpSet hs;
public void setViewData(View v) { // 獲取 HelpSet、DocumentBase 等上下文
this.myView = v;
this.doc = (HTMLDocument) v.getDocument();
this.base = doc.getBase();
// 向上查找 JHelpContentViewer 獲取 HelpSet
}
public void setAudioByID(String name) { // 透過 Map ID 獲取音檔 URL
Map map = hs.getCombinedMap();
URL url = map.getURLFromID(ID.create(name, hs));
AudioClip sound = Applet.newAudioClip(url);
sound.play();
}
}
<!-- HTML 主題檔嵌入 -->
<OBJECT CLASSID="java:LightWeightCom">
<param name="iconByID" value="top">
<param name="audioByID" value="music">
</OBJECT>
Slik_jni/ ├── classes/ # 編譯後 Java 類 ├── myskins/ # 範例皮膚資源 ├── native/ # JNI 庫 │ ├── Win32/ # .dll │ └── Linux/ # .so └── src/ # Java/C 原始碼
rundemo.bat / rundemo.sh 一鍵啟動JavaHelp 2.0 伺服器端僅支援 TOC/Index/Search 視圖,**不支援**次視窗、彈出視窗、輕量級元件。架構基於 Tomcat + JSP + JavaScript (tree.js, searchList.js) 實現 Web 版說明系統。
JNI + SLIK 證明了 Java 可以「零程式碼修改」同時在 Linux/Windows 達到原生 GUI 效能與非矩形視窗體驗。隨著 Linux 桌面佔有率提升,高階 UI 需求將爆發,SLIK 這類跨平台原生皮膚引擎極具潛力。對 Java 開發者而言,掌握 JNI 整合原生 UI 庫,將打破「Java 桌面效能差」的刻板印象,開啟全新應用場景。