背景
实际业务中经常需要展示某个网站, 并且希望在展示的时候单击网站可直接访问, 本节演示在表格中如何添加超链接支持.
需求
假设我需要渲染一个Study
类, 它只有三个属性id
,name
和website
, 其中id
只支持展示, name
只支持编辑, 而website
只支持单击时跳转到相应的网站, 效果图如下:
方案
要在表格中添加超链接支持, 需要特殊的CellLableProvider
, Jface
提供了ColumnLabelProvider
, 要想实现超链接, 只需要重新实现其中的update
和getText
方法.
方法名 | 方法签名 | 功能描述 |
---|---|---|
getText | String getText(Object element) | 提供表格渲染后展示的文本的内容 |
update | void update(ViewerCell cell) | 提供表格对象的额外展示项, 比如背景颜色, 字体的设置等等 |
我们的超链接支持就可以放在update
中来进行扩展实现:
- 创建
Link
对象, 并通过TableEditor
将其与TableItem
关联起来 - 添加
Link
对象的单击事件监听, 点击时进行跳转
Link link = new Link((Composite) cell.getControl(), SWT.NONE);
link.setText("<a>" + study.website + "</a>");
TableItem item = (TableItem) cell.getItem();
TableEditor editor = new TableEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
editor.setEditor(link, item, cell.getColumnIndex());
editor.layout();
link.addListener(SWT.Selection, e -> {try {Desktop.getDesktop().browse(new URI(study.website));} catch (IOException | URISyntaxException ex) {throw new RuntimeException(ex);}
});
注意实现
之前我们设置CellLabelProvider
时是通过Tabel
级别的对象进行的, 实际上我们往往是根据不同的Column
来设置更为个性化的展示的, 因此, 在创建Column
的同时设置CellLableProvider
更为合理. 伪代码:
TableViewer tableViewer = ...;
Table table = tableViewer.getTable();
TableColumn tableColumn = TableColumnFactory.newTableColumn(SWT.NONE)....create(table);
TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, tableColumn);
// 每一列设置一个特定的cellLabelProvider
tableViewerColumn.setLabelProvider(cellLabelProvider);
源码
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.widgets.TableColumnFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.*;import java.awt.*;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;public class Study {@Column(value = "ID", labelProviderClass = StudyLabelProvider.class)public int id;@Column(value = "名称", labelProviderClass = StudyLabelProvider.class, width = 200)@TextEditorpublic String name;@Column(value = "网站", labelProviderClass = StudyLabelProvider.class, width = 300)public String website;public Study(int id, String name, String website) {this.id = id;this.name = name;this.website = website;}private static Study[] studies() {return new Study[]{new Study(1, "github", "https://github.com"), new Study(2, "死磕Java", "https://skjava.com")};}public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {Display display = new Display();Shell shell = new Shell(display);shell.setLayout(new FillLayout());var tableViewer = new TableViewer(shell, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);Table table = tableViewer.getTable();table.setHeaderVisible(true);table.setLinesVisible(true);Field[] fields = Study.class.getFields();for (Field field : fields) {Column column = field.getAnnotation(Column.class);String columnText = column.value();Class<? extends CellLabelProvider> labelProviderClass = column.labelProviderClass();CellLabelProvider cellLabelProvider;if (!(CellLabelProvider.class == labelProviderClass)) {Constructor<? extends CellLabelProvider> constructor = labelProviderClass.getConstructor(String.class);cellLabelProvider = constructor.newInstance(columnText);} else {cellLabelProvider = new StudyLabelProvider(columnText);}TableColumn tableColumn = TableColumnFactory.newTableColumn(SWT.NONE).width(column.width()).text(columnText).align(SWT.CENTER).create(table);TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, tableColumn);tableViewerColumn.setLabelProvider(cellLabelProvider);EditingSupport editingSupport = null;TextEditor textEditor = field.getAnnotation(TextEditor.class);if (textEditor != null) {Class<? extends EditingSupport> editingSupportClass = textEditor.editingSupportClass();Constructor<? extends EditingSupport> constructor = editingSupportClass.getConstructor(TableViewerColumn.class);editingSupport = constructor.newInstance(tableViewerColumn);}Optional.ofNullable(editingSupport).ifPresent(tableViewerColumn::setEditingSupport);}ColumnViewerEditorActivationStrategy activationStrategy = new ColumnViewerEditorActivationStrategy(tableViewer) {@Overrideprotected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {// 只有双击事件才激活编辑器return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC || event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL;}};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.setInput(Study.studies());shell.open();while (!shell.isDisposed()) {if (!display.readAndDispatch()) {display.sleep();}}}public static class StudyEditingSupport extends EditingSupport {private final TableViewerColumn tableViewerColumn;private final String title;public StudyEditingSupport(TableViewerColumn tableViewerColumn) {super(tableViewerColumn.getViewer());this.tableViewerColumn = tableViewerColumn;TableColumn tableColumn = tableViewerColumn.getColumn();this.title = tableColumn.getText();}@Overrideprotected CellEditor getCellEditor(Object o) {return new TextCellEditor(tableViewerColumn.getColumn().getParent());}@Overrideprotected boolean canEdit(Object o) {return true;}@Overrideprotected Object getValue(Object o) {if (!(o instanceof Study study)) {return "";}return switch (title) {case "名称" -> study.name;default -> study.website;};}@Overrideprotected void setValue(Object o, Object o1) {if (!(o instanceof Study study)) {return;}switch (title) {case "名称" -> study.name = String.valueOf(o1);default -> study.website = String.valueOf(o1);}getViewer().refresh(o);}}public static class StudyLabelProvider extends ColumnLabelProvider {private final String title;public StudyLabelProvider(String title) {super();this.title = title;}@Overridepublic void update(ViewerCell cell) {if (!(cell.getElement() instanceof Study study)) {return;}var text = switch (title) {case "ID" -> String.valueOf(study.id);case "名称" -> study.name;default -> {Link link = new Link((Composite) cell.getControl(), SWT.NONE);link.setText("<a>" + study.website + "</a>");TableItem item = (TableItem) cell.getItem();TableEditor editor = new TableEditor(item.getParent());editor.grabHorizontal = true;editor.grabVertical = true;editor.setEditor(link, item, cell.getColumnIndex());editor.layout();link.addListener(SWT.Selection, e -> {try {Desktop.getDesktop().browse(new URI(study.website));} catch (IOException | URISyntaxException ex) {throw new RuntimeException(ex);}});yield "";}};cell.setText(text);}@Overridepublic String getText(Object element) {if (!(element instanceof Study study)) {return "";}return switch (title) {case "ID" -> String.valueOf(study.id);case "名称" -> study.name;default -> study.website;};}}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Column {String value();Class<? extends CellLabelProvider> labelProviderClass() default CellLabelProvider.class;int width() default 100;}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface TextEditor {Class<? extends EditingSupport> editingSupportClass() default StudyEditingSupport.class;}
}