前言
上節說到, 創建和渲染表格需要如下幾個步驟:
- 接收源數據數組(也可以是單個對象或者其他集合類型):
TableViewer.setInput(Object)
- 渲染接收的數據
- 渲染表頭:
TableViewer.setLabelProvider(IBaseLabelProvider)
- 渲染內容:
TableViewer.setContentProvider(IContentProvider)
- 渲染表頭:
在實際應用中我們往往需要編輯表格并實現雙向綁定, 本節內容主要集中講如何對表格單元格添加編輯支持.
需求
當雙擊單元格時進入編輯模式
步驟
添加編輯支持
Jface
提供了EditingSupport
抽象類, 可以很方便的實現, 不過需要配合TableViewerColumn
使用, 上節我們根據表頭數組創建了列:
String[] titles = {"ID", "姓名", "性別", "年齡"};// 創建列頭信息, 并最終綁定到tableArrays.stream(titles).forEach(title -> TableColumnFactory.newTableColumn(SWT.NONE).width(80).text(title).create(table));
如需添加編輯支持可以直接根據TableColumn
創建TableViewerColumn
并將每一列對應的EditingSupport
實現賦值給TableViewerColumn
即可.
實現EditingSupport
需要覆寫的方法簡介:
CellEditor getCellEditor(Object)
: 當前列對應的編輯器類型, 主要有:TextCellEditor
: 文字編輯器, 非常通用CheckboxCellEditor
: 復選編輯器ComboBoxCellEditor
: 下拉列表編輯器ColorCellEditor
: 顏色編輯器DialogCellEditor
: 對話編輯器, 這是高級用法, 可以實現個性定制
boolean canEdit(Object)
: 當前列是否支持編輯Object getValue(Object)
: 編輯初始狀態顯示的值void setValue(Object oldValue, Object newValue)
: 編輯結束時需要賦值的邏輯, 第一個參數為編輯前對應的值, 第二個為編輯后對應的新值, 值類型取決于編輯器, 比如TextCellEditor
對應的就是String
類型.
需要注意的是, 當我們接受新值后, 要刷新下當前表格, 否則界面展示依然是之前的值, 也就是說我們在
setValue
方法的最后需要加上一行tableViewer.update(o, null);
此時我們豐富一下創建表頭的邏輯, 這里列出空實現:
// 創建列頭信息, 并最終綁定到table
Arrays.stream(titles).forEach(title -> {TableColumn tableColumn = TableColumnFactory.newTableColumn(SWT.NONE).width(80).text(title).create(table);// 創建TableViewerColumn關聯到當前列并添加編輯支持new TableViewerColumn(tableViewer, tableColumn).setEditingSupport(new EditingSupport(tableViewer) {@Overrideprotected CellEditor getCellEditor(Object o) {return null;}@Overrideprotected boolean canEdit(Object o) {return false;}@Overrideprotected Object getValue(Object o) {return null;}@Overrideprotected void setValue(Object o, Object o1) {// 賦值邏輯...tableViewer.update(o, null);}});
});
添加觸發條件
僅僅添加編輯支持是不夠的, 因為系統不知道什么時候切換為編輯狀態, 比如我們只希望在雙擊當前單元格時開啟編輯狀態, Jface
提供了ColumnViewerEditorActivationStrategy
來控制策略:
ColumnViewerEditorActivationStrategy activationStrategy = new ColumnViewerEditorActivationStrategy(tableViewer) {@Overrideprotected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {// 只有雙擊事件才激活編輯器return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION;}
};
此時打開界面發現單擊表格也進入了編輯狀態, 并且一次性就高亮顯示整行, 這和需求不符, 我們需要借助TableViewerEditor
來強制激活此策略, 并集成TableViewerFocusCellManager
來高亮顯示本單元格而不是整行:
TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer, new FocusCellOwnerDrawHighlighter(tableViewer));
TableViewerEditor.create(tableViewer, focusCellManager, activationStrategy, ColumnViewerEditor.DEFAULT);
此時我們只需要將之前的EditingSupport
根據實際業務完善下基本就OK了, 整體源碼見下一小節, 先看下效果.
數據校驗
實際業務中可能對某些數據有特殊要求, 此時就要對輸入的數據進行校驗, 并給出提示, 我們可以借助MessageBox
來實現提示, 將校驗邏輯放在EdittingSupport.setValue
方法中.
比如對年齡的校驗可以這樣寫:
@Override
protected void setValue(Object o, Object o1) {String newValue = String.valueOf(o1);if (o instanceof People people) {switch (title) {case "年齡" -> {try {people.setAge(Integer.parseInt(newValue));} catch (Exception e) {MessageBox messageBox = new MessageBox(shell);messageBox.setText("輸入不合法");messageBox.setMessage("必須是數字");messageBox.open();}}// 其他邏輯}}tableViewer.update(o, null);
}
看下效果:
源碼
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.widgets.TableColumnFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;import java.util.Arrays;public class Main {public static void main(String[] args) {final Display display = Display.getDefault();final Shell shell = new Shell();shell.setLayout(new FillLayout());shell.setSize(500, 375);shell.setText("SWT Application");//注意這里,SWT.MULTI代表可以選擇多行,SWT.FULL_SELECTION代表可以整行選擇,SWT.BORDER邊框,SWT.V_SCROLL ,SWT.H_SCROLL滾動條TableViewer tableViewer = new TableViewer(shell, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);Table table = tableViewer.getTable();// 表格邊框線是否可見table.setLinesVisible(true);// 表頭是否可見table.setHeaderVisible(true);// 設置表格大小table.setBounds(97, 79, 373, 154);String[] titles = {"ID", "姓名", "性別", "年齡"};// 創建列頭信息, 并最終綁定到tableArrays.stream(titles).forEach(title -> {TableColumn tableColumn = TableColumnFactory.newTableColumn(SWT.NONE).width(80).text(title).create(table);new TableViewerColumn(tableViewer, tableColumn).setEditingSupport(new EditingSupport(tableViewer) {@Overrideprotected CellEditor getCellEditor(Object o) {return new TextCellEditor(tableViewer.getTable());}@Overrideprotected boolean canEdit(Object o) {return !"ID".equalsIgnoreCase(title);}@Overrideprotected Object getValue(Object o) {if (o instanceof People people) {return switch (title) {case "ID" -> String.valueOf(people.getId());case "姓名" -> people.getName();case "性別" -> people.getSex();case "年齡" -> String.valueOf(people.getAge());default -> "";};}return "";}@Overrideprotected void setValue(Object o, Object o1) {String newValue = String.valueOf(o1);if (o instanceof People people) {switch (title) {case "年齡" -> {try {people.setAge(Integer.parseInt(newValue));} catch (Exception e) {MessageBox messageBox = new MessageBox(shell);messageBox.setText("輸入不合法");messageBox.setMessage("必須是數字");messageBox.open();}}case "姓名" -> people.setName(newValue);case "性別" -> people.setSex(newValue);}}tableViewer.update(o, null);}});});ColumnViewerEditorActivationStrategy activationStrategy = new ColumnViewerEditorActivationStrategy(tableViewer) {@Overrideprotected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {// 只有雙擊事件才激活編輯器return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION;}};table.setHeaderBackground(display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND));table.setHeaderForeground(display.getSystemColor(SWT.COLOR_TITLE_FOREGROUND));TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer, new FocusCellOwnerDrawHighlighter(tableViewer));TableViewerEditor.create(tableViewer, focusCellManager, activationStrategy, ColumnViewerEditor.DEFAULT);tableViewer.setContentProvider(ArrayContentProvider.getInstance());tableViewer.setLabelProvider(PeopleLabelProvider.getInstance());People people = new People();people.setId(1);people.setName("張三");people.setSex("男");people.setAge(10);tableViewer.setInput(new People[]{people});shell.open();while (!shell.isDisposed()) {if (!display.readAndDispatch()) {display.sleep();}}}
}
其他方案
實現編輯支持還有其他方式, 比如實現ICellModifier
, 不過這種方式需要額外指定properties
用來指定和列名的對應關系, 個人不是很喜歡這種, 有興趣可以參考: Swt/Jface tableViewer入門教程三(加入在表格上直接編輯數據)