SWT/Jface(3): 表格中添加超链接

背景

实际业务中经常需要展示某个网站, 并且希望在展示的时候单击网站可直接访问, 本节演示在表格中如何添加超链接支持.

需求

假设我需要渲染一个Study类, 它只有三个属性id,namewebsite, 其中id只支持展示, name只支持编辑, 而website只支持单击时跳转到相应的网站, 效果图如下:
SWT/Jface(3): 表格中添加超链接_第1张图片

方案

要在表格中添加超链接支持, 需要特殊的CellLableProvider, Jface提供了ColumnLabelProvider, 要想实现超链接, 只需要重新实现其中的updategetText方法.

方法名 方法签名 功能描述
getText String getText(Object element) 提供表格渲染后展示的文本的内容
update void update(ViewerCell cell) 提供表格对象的额外展示项, 比如背景颜色, 字体的设置等等

我们的超链接支持就可以放在update中来进行扩展实现:

  1. 创建Link对象, 并通过TableEditor将其与TableItem关联起来
  2. 添加Link对象的单击事件监听, 点击时进行跳转
Link link = new Link((Composite) cell.getControl(), SWT.NONE);
link.setText("" + study.website + "");
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("" + study.website + "");
                    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;
    }
}

你可能感兴趣的:(swt学习,gui,swt,jface,java)