首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

SWT/JFace 瀏覽器組件實戰:構建功能完整的內嵌瀏覽器(上)

     編輯:Java 相關知識

前言

org.eclipse.swt.browser.Browser 是 SWT 提供的重量級組件,封裝系統原生瀏覽器引擎(Windows 上 IE/Edge WebView2、Linux 上 WebKitGTK、macOS 上 WebKit)。本文基於官方範例 BrowserExample,完整解析:工具列導航、位置欄、載入進度、狀態列、標題同步、彈出視窗處理、資源管理等核心功能實現。

架構概覽

  • FormLayout:精確控制工具列、位置欄、瀏覽器區、狀態列、進度條位置。
  • Browser:核心組件,支援導航、進度、狀態、標題、視窗事件監聽。
  • Canvas 動畫:載入中顯示旋轉圖示(忙碌指示器)。
  • 資源管理:圖片、顏色、字體的建立與釋放。

核心代碼結構

1. 主類與佈局初始化

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);

        // 事件綁定...
    }
}

2. 導航按鈕事件統一處理

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()));

3. Browser 事件監聽器完整配置

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"));
}

4. 彈出視窗處理

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();
        }
    });
}

5. 資源釋放

public void dispose() {
    freeResources();
}

void freeResources() {
    if (images != null) {
        for (Image img : images) {
            if (img != null) img.dispose();
        }
        images = null;
    }
    // Browser 由父容器自動釋放
}

關鍵技術點總結

1. Browser 可用性檢查

try {
    browser = new Browser(parent, SWT.NONE);
} catch (SWTError e) {
    // 系統無瀏覽器引擎(如無 WebKitGTK 的 Linux)
    // 降級顯示錯誤訊息或使用替代方案
}

2. FormLayout 精確佈局

  • FormAttachment(控件, 偏移, 對齊) 實現相對定位。
  • FormAttachment(百分比, 偏移) 實現響應式佔比。
  • 工具列固定頂部、Canvas 右上角、位置欄填滿中間、瀏覽器區填滿剩餘、狀態列/進度條固定底部。

3. 進度與忙碌狀態同步

  • ProgressListener.changed 更新進度條、設定 busy 旗標驅動 Canvas 動畫。
  • completed 重置進度條、按鈕狀態、停止動畫。

4. 彈出視窗遞歸模式

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/Colordispose() 中完整釋放

下篇預告

下篇將涵蓋:

  • JavaScript-Java 雙向通訊(Browser.addFunction / browser.execute)
  • 自定義資源協議處理
  • 列印/PDF 匯出
  • 安全策略配置(CSP、混合內容)
  • 效能優化與記憶體分析

總結

SWT Browser 組件提供了原生級瀏覽能力,核心在於:

  • FormLayout 構建專業瀏覽器 UI 佈局。
  • 事件監聽器體系 覆蓋導航、進度、狀態、標題、視窗生命週期。
  • 彈出視窗遞歸模式 完整支援 window.open。
  • 資源嚴格管理 避免原生資源洩漏。

此範例可作為嵌入式瀏覽器、幫助系統、Web 應用容器的直接起始代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved