org.eclipse.swt.browser.Browser 是 SWT 提供的重量級組件,封裝系統原生瀏覽器引擎(Windows 上 IE/Edge WebView2、Linux 上 WebKitGTK、macOS 上 WebKit)。本文基於官方範例 BrowserExample,完整解析:工具列導航、位置欄、載入進度、狀態列、標題同步、彈出視窗處理、資源管理等核心功能實現。
public class BrowserExample {
private Text location; // 位置欄
private Browser browser; // 瀏覽器核心
private Canvas canvas; // 忙碌動畫畫布
private Label status; // 狀態列文字
private ProgressBar progressBar; // 進度條
private Image[] images; // 動畫幀
private int index = 0;
private boolean busy = false;
public BrowserExample(Composite parent) {
Display display = parent.getDisplay();
FormLayout layout = new FormLayout();
parent.setLayout(layout);
// 建立工具列
ToolBar toolbar = new ToolBar(parent, SWT.NONE);
ToolItem itemBack = new ToolItem(toolbar, SWT.PUSH);
itemBack.setText("Back");
ToolItem itemForward = new ToolItem(toolbar, SWT.PUSH);
itemForward.setText("Forward");
ToolItem itemStop = new ToolItem(toolbar, SWT.PUSH);
itemStop.setText("Stop");
ToolItem itemRefresh = new ToolItem(toolbar, SWT.PUSH);
itemRefresh.setText("Refresh");
ToolItem itemGo = new ToolItem(toolbar, SWT.PUSH);
itemGo.setText("Go");
// 位置欄
location = new Text(parent, SWT.BORDER);
// 動畫 Canvas
images = new Image[]{new Image(display, "C:/icons/web/go.gif")};
canvas = new Canvas(parent, SWT.NO_BACKGROUND);
canvas.addPaintListener(e -> {
Point pt = canvas.getSize();
Rectangle rect = images[0].getBounds();
e.gc.drawImage(images[index], 0, 0, rect.width, rect.height, 0, 0, pt.x, pt.y);
});
canvas.addListener(SWT.MouseDown, e -> browser.setUrl(getResourceString("Startup")));
// 動畫定時器
display.asyncExec(new Runnable() {
public void run() {
if (canvas.isDisposed()) return;
if (busy) {
index = (index + 1) % images.length;
canvas.redraw();
}
display.timerExec(150, this);
}
});
// 狀態列與進度條
status = new Label(parent, SWT.NONE);
progressBar = new ProgressBar(parent, SWT.NONE);
// 佈局數據綁定
FormData data = new FormData();
data.top = new FormAttachment(0, 5);
toolbar.setLayoutData(data);
data = new FormData();
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
data.top = new FormAttachment(canvas, 5, SWT.DEFAULT);
data.bottom = new FormAttachment(status, -5, SWT.DEFAULT);
// 建立 Browser(可能失敗需捕獲 SWTError)
try {
browser = new Browser(parent, SWT.NONE);
browser.setLayoutData(data);
} catch (SWTError e) {
Label label = new Label(parent, SWT.CENTER | SWT.WRAP);
label.setText("Browser not available");
label.setLayoutData(data);
}
// Canvas 位置(右上角)
data = new FormData();
data.width = 24; data.height = 24;
data.top = new FormAttachment(0, 5);
data.right = new FormAttachment(100, -5);
canvas.setLayoutData(data);
// 位置欄位置
data = new FormData();
data.top = new FormAttachment(toolbar, 0, SWT.TOP);
data.left = new FormAttachment(toolbar, 5, SWT.RIGHT);
data.right = new FormAttachment(canvas, -5, SWT.DEFAULT);
location.setLayoutData(data);
// 狀態列與進度條位置
data = new FormData();
data.left = new FormAttachment(0, 5);
data.right = new FormAttachment(progressBar, 0, SWT.DEFAULT);
data.bottom = new FormAttachment(100, -5);
status.setLayoutData(data);
data = new FormData();
data.right = new FormAttachment(100, -5);
data.bottom = new FormAttachment(100, -5);
progressBar.setLayoutData(data);
// 事件綁定...
}
}
Listener navListener = e -> {
ToolItem item = (ToolItem) e.widget;
if (item == itemBack) browser.back();
else if (item == itemForward) browser.forward();
else if (item == itemStop) browser.stop();
else if (item == itemRefresh) browser.refresh();
else if (item == itemGo) browser.setUrl(location.getText());
};
itemBack.addListener(SWT.Selection, navListener);
itemForward.addListener(SWT.Selection, navListener);
itemStop.addListener(SWT.Selection, navListener);
itemRefresh.addListener(SWT.Selection, navListener);
itemGo.addListener(SWT.Selection, navListener);
// Enter 鍵導航
location.addListener(SWT.DefaultSelection, e -> browser.setUrl(location.getText()));
if (browser != null) {
// 初始按鈕狀態
itemBack.setEnabled(browser.isBackEnabled());
itemForward.setEnabled(browser.isForwardEnabled());
// LocationListener:URL 變更
browser.addLocationListener(new LocationListener() {
public void changed(LocationEvent event) {
busy = true;
if (event.top) location.setText(event.location); // 只更新頂層框架 URL
}
public void changing(LocationEvent event) {}
});
// ProgressListener:載入進度
browser.addProgressListener(new ProgressListener() {
public void changed(ProgressEvent event) {
if (event.total == 0) return;
int ratio = event.current * 100 / event.total;
progressBar.setSelection(ratio);
busy = event.current != event.total;
if (!busy) { index = 0; canvas.redraw(); }
}
public void completed(ProgressEvent event) {
itemBack.setEnabled(browser.isBackEnabled());
itemForward.setEnabled(browser.isForwardEnabled());
progressBar.setSelection(0);
busy = false;
index = 0;
canvas.redraw();
}
});
// StatusTextListener:狀態列文字
browser.addStatusTextListener(event -> status.setText(event.text));
// TitleListener:視窗標題同步(僅當父容器為 Shell)
if (parent instanceof Shell) {
Shell shell = (Shell) parent;
browser.addTitleListener(event ->
shell.setText(event.title + " - " + getResourceString("window.title")));
}
// 初始化視窗處理
initialize(display, browser);
// 載入首頁
browser.setUrl(getResourceString("Startup"));
}
static void initialize(Display display, Browser browser) {
// 開新視窗
browser.addOpenWindowListener(new OpenWindowListener() {
public void open(WindowEvent event) {
System.out.println("Open");
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
Browser newBrowser = new Browser(shell, SWT.NONE);
initialize(display, newBrowser); // 遞歸配置
event.browser = newBrowser; // 關鍵:將新 Browser 返回給事件
}
});
// 顯示/隱藏視窗
browser.addVisibilityWindowListener(new VisibilityWindowListener() {
public void hide(WindowEvent event) {}
public void show(WindowEvent event) {
System.out.println("Show");
Browser b = (Browser) event.widget;
Shell shell = b.getShell();
if (event.location != null) shell.setLocation(event.location);
if (event.size != null) {
Point size = event.size;
shell.setSize(shell.computeSize(size.x, size.y));
}
shell.open();
}
});
// 關閉視窗
browser.addCloseWindowListener(new CloseWindowListener() {
public void close(WindowEvent event) {
System.out.println("Close");
Browser b = (Browser) event.widget;
Shell shell = b.getShell();
shell.close();
}
});
}
public void dispose() {
freeResources();
}
void freeResources() {
if (images != null) {
for (Image img : images) {
if (img != null) img.dispose();
}
images = null;
}
// Browser 由父容器自動釋放
}
try {
browser = new Browser(parent, SWT.NONE);
} catch (SWTError e) {
// 系統無瀏覽器引擎(如無 WebKitGTK 的 Linux)
// 降級顯示錯誤訊息或使用替代方案
}
FormAttachment(控件, 偏移, 對齊) 實現相對定位。FormAttachment(百分比, 偏移) 實現響應式佔比。ProgressListener.changed 更新進度條、設定 busy 旗標驅動 Canvas 動畫。completed 重置進度條、按鈕狀態、停止動畫。OpenWindowListener 中建立新 Shell + Browser,遞歸呼叫 initialize,將新 Browser 指派給 event.browser。這是支援 window.open()、target=_blank 的標準模式。
| 問題 | 原因 | 解決 |
|---|---|---|
| Browser 建立失敗 | 系統缺少 WebKit/IE/Edge WebView2 | 安裝對應運行時;或改用 SWT.MOZILLA (舊) / 外部瀏覽器啟動 |
| JavaScript 錯誤/不支援 | 引擎版本過舊 | Windows 確保安裝 WebView2 Runtime;Linux 安裝 webkit2gtk-4.0 |
| 彈出視窗不工作 | 未實作 OpenWindowListener | 必須實作並設定 event.browser |
| Cookie/Session 不共享 | 各 Browser 實例獨立存儲 | 需自行同步或使用單例 Browser |
| 記憶體洩漏 | 未釋放 Image/Font/Color | dispose() 中完整釋放 |
下篇將涵蓋:
Browser.addFunction / browser.execute)SWT Browser 組件提供了原生級瀏覽能力,核心在於:
window.open。此範例可作為嵌入式瀏覽器、幫助系統、Web 應用容器的直接起始代碼。