背景
实际业务中经常需要展示某个网站, 并且希望在展示的时候单击网站可直接访问, 本节演示在表格中如何添加超链接支持.
需求
假设我需要渲染一个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)
@TextEditor
public 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) {
@Override
protected 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();
}
@Override
protected CellEditor getCellEditor(Object o) {
return new TextCellEditor(tableViewerColumn.getColumn().getParent());
}
@Override
protected boolean canEdit(Object o) {
return true;
}
@Override
protected Object getValue(Object o) {
if (!(o instanceof Study study)) {
return "";
}
return switch (title) {
case "名称" -> study.name;
default -> study.website;
};
}
@Override
protected 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;
}
@Override
public 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);
}
@Override
public 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;
}
}