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

SWT/JFace BugTracker 完整實作:從 SWT TableEditor 到 JFace TableViewer 架構演進

     編輯:Java 相關知識

前言

BugTracker 是展示 SWT/JFace 表格元件進階用法的經典範例。本文完整呈現兩個版本的演進:版本一 使用純 SWT Table + TableEditor 實現內嵌編輯、排序、檔案持久化;版本二 引入 JFace TableViewer 架構,導入領域模型、ContentProvider/LabelProvider/CellEditor/ICellModifier/ViewerFilter/ViewerSorter,實現關注點分離與高擴充性。兩版本功能對等,架構差異顯著,適合作為技術選型參考。

共同功能需求

  • 顯示 Bug 清單:ID、Summary、Assigned to、Solved 四欄
  • 新增、刪除、儲存(持久化至 bugs.dat)
  • 欄位點擊排序
  • 雙擊/編輯單元格:文字欄用文字框、Solved 欄用核取方塊
  • 工具列操作:新增、刪除、儲存、顯示未解決

版本一:純 SWT 實作(BugTracker.java)

UI 建構


GridLayout layout = new GridLayout(); shell.setLayout(layout);
ToolBar toolBar = new ToolBar(shell, SWT.RIGHT | SWT.FLAT);
ToolBarManager mgr = new ToolBarManager(toolBar);
mgr.add(new Action("New bug") { public void run() { TableItem item=new TableItem(table,SWT.NULL); item.setImage(bugIcon); table.select(table.getItemCount()-1); }});
mgr.add(new Action("Delete selected") { public void run() { /* 確認對話框後 table.remove(index) */ }});
mgr.add(new Separator()); mgr.add(new Action("Save") { public void run() { saveBugs(); }});
mgr.update(true);

table = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
table.setLayoutData(new GridData(GridData.FILL_BOTH));
table.setLinesVisible(true); table.setHeaderVisible(true);
// 四欄 TableColumn 設定...
table.pack();

TableEditor 內嵌編輯(滑鼠點擊觸發)


final TableEditor editor = new TableEditor(table);
table.addListener(SWT.MouseDown, e -> {
    Point pt = new Point(e.x, e.y);
    TableItem item = table.getItem(pt); if (item==null) return;
    int col = -1; for(int i=0;i{ item.setText(3,btn.getSelection()?"YES":"NO"); btn.dispose(); });
        btn.addListener(SWT.Traverse, ev->{ if(ev.detail==SWT.TRAVERSE_RETURN||ev.detail==SWT.TRAVERSE_ESCAPE){btn.dispose();ev.doit=false;} });
        btn.setFocus();
    } else if (col>=0 && col<3) { // Text columns
        Text txt = new Text(table, SWT.NONE);
        txt.setText(item.getText(col));
        editor.horizontalAlignment=SWT.LEFT; editor.grabHorizontal=true;
        editor.setEditor(txt, item, col);
        txt.addListener(SWT.FocusOut, ev->{ item.setText(col,txt.getText()); txt.dispose(); });
        txt.addListener(SWT.Traverse, ev->{ if(ev.detail==SWT.TRAVERSE_RETURN||ev.detail==SWT.TRAVERSE_ESCAPE){txt.dispose();ev.doit=false;} });
        txt.setFocus();
    }
});

排序:Listener + Comparator + 重建 TableItem


Listener sortListener = e -> { if(e.widget instanceof TableColumn) sortTable(table, table.indexOf((TableColumn)e.widget)); };
for(int i=0;i i.getText(col)));
    for(TableItem old: items) {
        TableItem nu = new TableItem(table, SWT.NULL);
        for(int j=0;j

持久化:DataOutputStream/DataInputStream


private void saveBugs() {
    try(DataOutputStream out=new DataOutputStream(new FileOutputStream("bugs.dat"))) {
        for(int i=0;i

版本二:JFace TableViewer 架構(BugTrackerJFace.java)

領域模型


public static class Bug {
    public String id, summary, assignedTo; public boolean isSolved;
    public Bug(String id,String sum,String ass,boolean sol){this.id=id;this.summary=sum;this.assignedTo=ass;this.isSolved=sol;}
    public static Vector loadBugs(File file){ /* 同版本一讀取邏輯,回傳 Vector */ }
}

TableViewer 組裝


tableViewer = new TableViewer(table);
tableViewer.setContentProvider(new IStructuredContentProvider() {
    public Object[] getElements(Object in) { return ((Vector)in).toArray(); }
    public void dispose(){} public void inputChanged(Viewer v,Object o,Object n){}
});
tableViewer.setLabelProvider(new ITableLabelProvider() {
    public Image getColumnImage(Object el,int col){ return col==0?bugIcon:null; }
    public String getColumnText(Object el,int col){
        Bug b=(Bug)el; return switch(col){case 0->b.id;case 1->b.summary;case 2->b.assignedTo;case 3->b.isSolved?"YES":"NO";default->null;};
    }
    // 其餘介面方法空實作
});
tableViewer.setColumnProperties(new String[]{"ID","Summary","Assigned to","Solved"});

CellEditor[] editors = { new TextCellEditor(table), new TextCellEditor(table), new TextCellEditor(table), new CheckboxCellEditor(table) };
tableViewer.setCellEditors(editors);
tableViewer.setCellModifier(new ICellModifier() {
    public boolean canModify(Object el,String prop){ return true; }
    public Object getValue(Object el,String prop){
        Bug b=(Bug)el; return switch(prop){case "ID"->b.id;case "Summary"->b.summary;case "Assigned to"->b.assignedTo;case "Solved"->b.isSolved;default->null;};
    }
    public void modify(Object el,String prop,Object val){
        Bug b = (el instanceof TableItem) ? (Bug)((TableItem)el).getData() : (Bug)el;
        switch(prop){case "ID"->b.id=(String)val;case "Summary"->b.summary=(String)val;case "Assigned to"->b.assignedTo=(String)val;case "Solved"->b.isSolved=(Boolean)val;}
        tableViewer.update(b,null);
    }
});

過濾與排序


ViewerFilter unsolvedFilter = new ViewerFilter() {
    public boolean select(Viewer v,Object p,Object el){ return !((Bug)el).isSolved; }
};
Action showUnsolved = new Action("Show unsolved only", IAction.AS_CHECK_BOX) {
    public void run(){ if(isChecked()) tableViewer.addFilter(unsolvedFilter); else tableViewer.removeFilter(unsolvedFilter); }
};

class BugSorter extends ViewerSorter {
    private int idx; public BugSorter(String prop){ for(int i=0;ib1.id.compareTo(b2.id);case 1->b1.summary.compareTo(b2.summary);case 2->b1.assignedTo.compareTo(b2.assignedTo);case 3->Boolean.compare(b1.isSolved,b2.isSolved);default->0;};
    }
}
tcID.addListener(SWT.Selection, e->tableViewer.setSorter(new BugSorter(colNames[0])));
// ... 其他欄位同理

資料載入


bugs = Bug.loadBugs(new File("bugs.dat"));
tableViewer.setInput(bugs);

架構演進差異深度解析

層面版本一 (SWT)版本二 (JFace)影響
資料儲存TableItem 文字/圖示獨立 Bug 物件 (Vector)資料與 UI 解耦,易單元測試、序列化、匯出
編輯模型TableEditor + 手動 ListenerCellEditor + ICellModifier編輯邏輯集中、可複用、支援驗證
排序實作Comparator + 重建 TableItemViewerSorter + setSorter聲明式、自動處理視圖更新
過濾實作無 (需手動隱藏)ViewerFilter + add/removeFilter動態切換、多重過濾疊加
資料同步開發者手動維護update(element) 自動重繪減少 Bug、支援多視圖同步
程式碼結構單類別 400+ 行內部類別分層、職責清晰可維護性、可讀性顯著提升

效能考量

  • 大量資料(>10,000 列):TableViewer 預設載入所有元素,建議實作 ILazyContentProvider 或分頁載入。
  • 頻繁更新:tableViewer.update(element, null) 僅重繪該列;refresh() 會重新查詢 ContentProvider,成本較高。
  • 記憶體:Bug 物件輕量,Vector 適合中小資料量;大資料量改用 ArrayList 或資料庫游標。

遷移建議

  1. 先建立領域模型(Bug)與載入/儲存服務。
  2. 替換 Table 為 TableViewer,設定 ContentProvider/LabelProvider。
  3. 逐步遷移編輯邏輯至 CellEditor/ICellModifier。
  4. 加入 ViewerSorter/ViewerFilter 替換手動排序/過濾。
  5. 移除所有直接操作 TableItem 的程式碼,改操作領域物件並呼叫 update()。

總結

BugTracker 雙版本完美展示了從「UI 中心」到「資料中心」的架構演進。JFace Viewer 框架雖增加初期學習成本,但其標準化的 Provider/Editor/Filter/Sorter 介面,能讓複雜表格應用的開發效率與維護性大幅提升。建議團隊在專案早期評估複雜度,果斷選擇適合的架構起步。

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