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

SWT/JFace ApplicationWindow 實作:檔案檢視器與非同步行數統計

     編輯:Java 相關知識

前言

JFace ApplicationWindow 為獨立視窗應用提供標準化框架:選單列、工具列、狀態列、內容區域自動佈局。本文解析一個 FileViewer 範例,展示檔案載入、非同步進度回報(IRunnableWithProgress)、行數統計與資源管理完整流程。

ApplicationWindow 結構

public class FileViewer extends ApplicationWindow {
    Text text; String content; String lineDelimiter;
    
    public FileViewer(Shell parentShell) {
        super(parentShell);
        addMenuBar(); addStatusLine(); addToolBar(SWT.FLAT);
    }
    
    protected Control createContents(Composite parent) {
        getShell().setText("FileViewer v2.0");
        setStatus("Ready");
        text = new Text(parent, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        text.setSize(300, 200);
        return text; // 回傳內容區域控制項
    }
}
  • addMenuBar()、addToolBar()、addStatusLine():啟用標準 UI 區域
  • createContents:建立客戶區內容,此處為多行 Text
  • createMenuManager、createToolBarManager、createStatusLineManager:覆寫以自訂動作

動作定義與貢獻

開啟檔案

Action actionOpenFile = new Action("Open", ImageDescriptor.createFromFile(null, "icons/open.gif")) {
    public void run() {
        FileDialog dialog = new FileDialog(getShell(), SWT.OPEN);
        String file = dialog.open();
        if (file != null) {
            try {
                String content = readFileAsAString(new File(file));
                text.setText(content);
                setStatus("File loaded successfully: " + file);
            } catch (IOException e) {
                e.printStackTrace(); setStatus("Failed to load file: " + file);
            }
        }
    }
};

非同步行數統計(IRunnableWithProgress)

IRunnableWithProgress runnableWithProgress = monitor -> {
    System.out.println("Running from thread: " + Thread.currentThread().getName());
    // 取得文字內容需在 UI 執行緒
    getShell().getDisplay().syncExec(() -> {
        content = text.getText();
        lineDelimiter = text.getLineDelimiter();
    });
    
    monitor.beginTask("Counting total number of lines", content.length());
    int lines = 1;
    for (int i = 0; i < content.length(); i++) {
        if (monitor.isCanceled()) { monitor.done(); System.out.println("Action cancelled"); return; }
        if (i + lineDelimiter.length() < content.length() &&
            lineDelimiter.equals(content.substring(i, i + lineDelimiter.length()))) {
            lines++;
        }
        monitor.worked(1);
        Thread.sleep(1); // 模擬耗時運算
    }
    monitor.done();
    System.out.println("Total number of lines: " + lines);
};

Action actionCount = new Action("Count", ImageDescriptor.createFromFile(null, "icons/run.gif")) {
    public void run() {
        try {
            // fork=true: 背景執行緒; cancelable=true: 可取消; runnable: 任務
            FileViewer.this.run(true, true, runnableWithProgress);
        } catch (InvocationTargetException | InterruptedException e) { e.printStackTrace(); }
    }
};

關鍵點解析

  1. fork=true:在背景執行緒執行 run(),不阻塞 UI;進度對話框顯示於前景。
  2. syncExec 取得 UI 資料:Text.getText() 必須在 UI 執行緒呼叫,故用 display.syncExec 切回主執行緒。
  3. monitor.isCanceled():使用者點擊進度對話框「取消」時回傳 true,任務應主動結束並呼叫 monitor.done()。
  4. monitor.worked(1):報告進度增量;建構時 beginTask(taskName, totalWork) 設定總工作量。
  5. Thread.sleep(1):模擬運算耗時,實際專案移除或替換為真實邏輯。

選單與工具列組裝


protected MenuManager createMenuManager() {
    MenuManager menuManager = new MenuManager("");
    MenuManager fileMenu = new MenuManager("&File");
    fileMenu.add(actionOpenFile);
    menuManager.add(fileMenu);
    MenuManager toolsMenu = new MenuManager("&Tools");
    toolsMenu.add(actionCount);
    menuManager.add(toolsMenu);
    return menuManager;
}

protected ToolBarManager createToolBarManager(int style) {
    ToolBarManager mgr = new ToolBarManager(style);
    mgr.add(actionOpenFile); mgr.add(actionCount);
    return mgr;
}

檔案讀取工具方法


public static String readFileAsAString(File file) throws IOException {
    return new String(getBytesFromFile(file));
}

public static byte[] getBytesFromFile(File file) throws IOException {
    try (InputStream is = new FileInputStream(file)) {
        long length = file.length();
        if (length > Integer.MAX_VALUE)
            throw new IllegalArgumentException("File too large (>= 2GB)");
        byte[] bytes = new byte[(int) length];
        int offset = 0, numRead;
        while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0)
            offset += numRead;
        if (offset < bytes.length)
            throw new IOException("Could not completely read file " + file.getName());
        return bytes;
    }
}

使用 try-with-resources 確保流關閉;分塊讀取避免大檔案單次分配記憶體壓力(雖此處仍一次性分配位元組陣列,超大檔案建議改用串流處理)。

啟動入口


public static void main(String[] args) {
    ApplicationWindow viewer = new FileViewer(null);
    viewer.setBlockOnOpen(true); // 阻塞直到視窗關閉
    viewer.open();
}

ApplicationWindow 核心優勢總結

特性說明
標準化 UI 區域選單列、工具列、狀態列、內容區自動佈局
動作貢獻模型Action 一次定義,同時出現在選單/工具列/右鍵選單
進度回報整合run(fork, cancelable, runnable) 內建進度對話框與取消支援
生命週期管理open()/close()、setBlockOnOpen 封裝事件迴圈
狀態列管理StatusLineManager 統一管理狀態文字、進度條

最佳實務建議

  1. 長時間任務必須 fork=true:避免 UI 凍結,提供取消機制提升使用者體驗。
  2. UI 存取務必 syncExec/asyncExec:背景執行緒直接操作 SWT 控制項會拋出 SWTException: Invalid thread access。
  3. 大檔案採用串流/分頁載入:Text.setText 會一次載入全部字串,超大檔案會導致 OOM,建議改用 StyledText 配合虛擬載入或分頁顯示。
  4. 資源路徑使用 ClassLoader:ImageDescriptor.createFromFile(FileViewer.class, "icons/open.gif") 支援 JAR 內資源,部署更彈性。

總結

本範例展示了 JFace ApplicationWindow 快速構建標準視窗應用的能力,結合 IRunnableWithProgress 實現非同步任務與進度回報。開發者可在此基礎上擴充:編輯功能、語法高亮、多標籤、最近開啟清單等,打造完整的文字編輯/檢視工具。

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