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

SWT/JFace 表格編輯器實作:TableEditor 內嵌編輯與 JFace TableViewer 架構對比

     編輯:Java 相關知識

前言

表格內嵌編輯是桌面應用常見需求。SWT 提供 TableEditor 實現單元格級編輯器(文字框、核取方塊等);JFace TableViewer 則提供更高層抽象:CellEditor、ICellModifier、ViewerFilter、ViewerSorter。本文透過 BugTracker(純 SWT)與 BugTrackerJFace 兩個版本,深度對比兩種架構的實作差異與優劣。

版本一:BugTracker(純 SWT + TableEditor)

資料模型:直接存在 TableItem


Table table = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
TableColumn tcID = new TableColumn(table, SWT.LEFT); tcID.setText("ID"); tcID.setWidth(60);
TableColumn tcSummary = new TableColumn(table, SWT.NULL); tcSummary.setText("Summary"); tcSummary.setWidth(200);
TableColumn tcAssigned = new TableColumn(table, SWT.NULL); tcAssigned.setText("Assigned to"); tcAssigned.setWidth(80);
TableColumn tcSolved = new TableColumn(table, SWT.NULL); tcSolved.setText("Solved"); tcSolved.setWidth(50);

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 column = -1;
    for (int i = 0; i < table.getColumnCount(); i++)
        if (item.getBounds(i).contains(pt)) column = i;
    if (column == -1) return;
    
    if (column == 3) { // Solved 欄:核取方塊
        Button btn = new Button(table, SWT.CHECK);
        btn.setSelection(item.getText(3).equalsIgnoreCase("YES"));
        editor.horizontalAlignment = SWT.LEFT;
        editor.grabHorizontal = true;
        editor.setEditor(btn, item, column);
        btn.addListener(SWT.FocusOut, ev -> { 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) { item.setText(3, btn.getSelection()?"YES":"NO"); btn.dispose(); ev.doit=false; }});
        btn.setFocus();
    } else if (column < 3) { // 文字欄:文字框
        Text txt = new Text(table, SWT.NONE);
        txt.setText(item.getText(column));
        editor.horizontalAlignment = SWT.LEFT; editor.grabHorizontal = true;
        editor.setEditor(txt, item, column);
        txt.addListener(SWT.FocusOut, ev -> { item.setText(column, 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();
    }
});

關鍵點:

  • TableEditor 一次只能編輯一個單元格,setEditor(control, item, column) 綁定位置。
  • grabHorizontal=true 讓編輯器寬度跟隨欄位寬度。
  • 焦點離開(FocusOut)或按 Enter/Esc(Traverse)時寫回資料並銷毀編輯器。

排序實作(Listener + Comparator)


Listener sortListener = e -> {
    if (!(e.widget instanceof TableColumn)) return;
    TableColumn tc = (TableColumn) e.widget;
    sortTable(table, table.indexOf(tc));
};
for (int i=0; i i.getText(colIndex)));
    // 重建項目順序(TableItem 不支援直接移動)
    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

版本二:BugTrackerJFace(TableViewer 架構)

領域模型分離


public static class Bug {
    public String id, summary, assignedTo; public boolean isSolved;
    public Bug(String id, String summary, String assignedTo, boolean solved) { ... }
    public static Vector loadBugs(File file) { ... } // 讀取 bugs.dat 回傳 Vector
}

ContentProvider:提供資料陣列


tableViewer.setContentProvider(new IStructuredContentProvider() {
    public Object[] getElements(Object input) { return ((Vector)input).toArray(); }
    public void dispose() {} public void inputChanged(Viewer v, Object old, Object neu) {}
});

LabelProvider:欄位文字與圖示


tableViewer.setLabelProvider(new ITableLabelProvider() {
    public Image getColumnImage(Object element, int col) { return col==0 ? bugIcon : null; }
    public String getColumnText(Object element, int col) {
        Bug b = (Bug) element;
        return switch(col) { case 0 -> b.id; case 1 -> b.summary; case 2 -> b.assignedTo; case 3 -> b.isSolved?"YES":"NO"; default -> null; };
    }
    // 其他介面方法空實作
});

CellEditor 與 ICellModifier:編輯支援


CellEditor[] editors = new CellEditor[4];
editors[0] = editors[1] = editors[2] = new TextCellEditor(table);
editors[3] = new CheckboxCellEditor(table);
tableViewer.setCellEditors(editors);
tableViewer.setColumnProperties(new String[]{"ID","Summary","Assigned to","Solved"});

tableViewer.setCellModifier(new ICellModifier() {
    public boolean canModify(Object element, String property) { return true; }
    public Object getValue(Object element, String property) {
        Bug b = (Bug) element;
        return switch(property) { case "ID" -> b.id; case "Summary" -> b.summary; case "Assigned to" -> b.assignedTo; case "Solved" -> b.isSolved; default -> null; };
    }
    public void modify(Object element, String property, Object value) {
        Bug b = (element instanceof TableItem) ? (Bug)((TableItem)element).getData() : (Bug)element;
        switch(property) { case "ID" -> b.id=(String)value; case "Summary" -> b.summary=(String)value; case "Assigned to" -> b.assignedTo=(String)value; case "Solved" -> b.isSolved=(Boolean)value; }
        tableViewer.update(b, null); // 通知 Viewer 重繪該元素
    }
});

優勢: 編輯邏輯作用於領域物件(Bug),而非 UI 元件(TableItem),資料與視圖分離。

ViewerFilter:未解決過濾


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

ViewerSorter:欄位排序


class BugSorter extends ViewerSorter {
    private int propIndex;
    public BugSorter(String prop) { for(int i=0;i b1.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); // 觸發 ContentProvider.getElements

架構對比總結

維度SWT TableEditorJFace TableViewer
資料模型隱含在 TableItem 文字/圖示獨立領域物件
編輯器管理手動 TableEditor + ListenerCellEditor 陣列 + ICellModifier
排序手動 Comparator + 重建 TableItemViewerSorter + setSorter
過濾手動隱藏/移除項目ViewerFilter + addFilter/removeFilter
資料同步開發者自行維護update(element) 自動重繪
程式碼量較多(UI 邏輯混雜)較少(關注點分離)
學習曲線低(直觀)中(需理解 Viewer 架構)
擴充性差(大量複製貼上)優(可複用 Provider/Editor/Filter/Sorter)

選型建議

  • 簡單表格、一次性專案、團隊不熟 JFace:使用 TableEditor 快速交付。
  • 中大型專案、需排序/過濾/分頁/匯出、長期維護:投資 TableViewer 架構,長期收益顯著。
  • 漸進式遷移:可先引入 TableViewer 僅作為顯示層,保留 TableEditor 編輯,逐步替換。

總結

本文透過 BugTracker 雙版本對比,展示了從「UI 綁定資料」到「資料驅動 UI」的架構演進。JFace Viewer 框架雖有學習門檻,但其 ContentProvider/LabelProvider/CellEditor/Filter/Sorter 的關注點分離設計,能大幅降低複雜表格應用的維護成本,是專業 SWT/JFace 開發者的必備技能。

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