java - JTable 中 JEditorPane 中的超链接

标签 java html swing jtable jeditorpane

我发誓...我希望这是我最后一个这样问的问题,但我快要发疯了。

我有一个使用自定义 TableCellRenderer 的 JTable,它使用 JEditorPane 在 JTable 的各个单元格中显示 html。我如何处理点击 JEditorPane 中显示的链接?

我知道 HyperlinkListener,但没有鼠标事件通过 JTable 到达 EditorPane 以处理任何 HyperlinkEvents。

如何在 JTable 中的 JEditorPane 中处理超链接?

最佳答案

EditorPane 没有接收到任何事件,因为从 TableCellRenderer 返回的组件只允许显示,而不是拦截事件,这使得它几乎与图像相同,但不允许任何行为。因此,即使注册了监听器,返回的组件也永远不会“意识到”任何事件。解决方法是在 JTable 上注册一个 MouseListener,并从那里拦截所有相关事件。

这是我过去创建的一些类,用于允许 JButton 翻转在 JTable 中工作,但您也应该能够重新使用其中的大部分来解决您的问题。对于需要它的每个单元格,我都有一个单独的 JButton。这样,这个 ActiveJComponentTableMouseListener 就会计算出鼠标事件发生在哪个单元格中,并将事件分派(dispatch)给相应的组件。 ActiveJComponentTableCellRenderer 的工作是通过 Map 跟踪组件。

它足够智能,可以知道何时已触发事件,因此您不会积压冗余事件。为超文本实现这一点应该没有什么不同,您可能仍然希望翻转。这是类(class)

public class ActiveJComponentTableMouseListener extends MouseAdapter implements MouseMotionListener {

private JTable table;
private JComponent oldComponent = null;
private TableCell oldTableCell = new TableCell();

public ActiveJComponentTableMouseListener(JTable table) {
    this.table = table;
}

@Override
public void mouseMoved(MouseEvent e) {
    TableCell cell = new TableCell(getRow(e), getColumn(e));

    if (alreadyVisited(cell)) {
        return;
    }
    save(cell);

    if (oldComponent != null) {
        dispatchEvent(createMouseEvent(e, MouseEvent.MOUSE_EXITED), oldComponent);
        oldComponent = null;
    }

    JComponent component = getComponent(cell);
    if (component == null) {
        return;
    }
    dispatchEvent(createMouseEvent(e, MouseEvent.MOUSE_ENTERED), component);
    saveComponent(component);
    save(cell);
}

@Override
public void mouseExited(MouseEvent e) {
    TableCell cell = new TableCell(getRow(e), getColumn(e));

    if (alreadyVisited(cell)) {
        return;
    }
    if (oldComponent != null) {
        dispatchEvent(createMouseEvent(e, MouseEvent.MOUSE_EXITED), oldComponent);
        oldComponent = null;
    }
}

@Override
public void mouseEntered(MouseEvent e) {
    forwardEventToComponent(e);
}

private void forwardEventToComponent(MouseEvent e) {
    TableCell cell = new TableCell(getRow(e), getColumn(e));
    save(cell);
    JComponent component = getComponent(cell);
    if (component == null) {
        return;
    }
    dispatchEvent(e, component);
    saveComponent(component);
}

private void dispatchEvent(MouseEvent componentEvent, JComponent component) {
    MouseEvent convertedEvent = (MouseEvent) SwingUtilities.convertMouseEvent(table, componentEvent, component);
    component.dispatchEvent(convertedEvent);
    // This is necessary so that when a button is pressed and released
    // it gets rendered properly.  Otherwise, the button may still appear
    // pressed down when it has been released.
    table.repaint();
}

private JComponent getComponent(TableCell cell) {
    if (rowOrColumnInvalid(cell)) {
        return null;
    }
    TableCellRenderer renderer = table.getCellRenderer(cell.row, cell.column);

    if (!(renderer instanceof ActiveJComponentTableCellRenderer)) {
        return null;
    }
    ActiveJComponentTableCellRenderer activeComponentRenderer = (ActiveJComponentTableCellRenderer) renderer;

    return activeComponentRenderer.getComponent(cell);
}

private int getColumn(MouseEvent e) {
    TableColumnModel columnModel = table.getColumnModel();
    int column = columnModel.getColumnIndexAtX(e.getX());
    return column;
}

private int getRow(MouseEvent e) {
    int row = e.getY() / table.getRowHeight();
    return row;
}

private boolean rowInvalid(int row) {
    return row >= table.getRowCount() || row < 0;
}

private boolean rowOrColumnInvalid(TableCell cell) {
    return rowInvalid(cell.row) || columnInvalid(cell.column);
}

private boolean alreadyVisited(TableCell cell) {
    return oldTableCell.equals(cell);
}

private boolean columnInvalid(int column) {
    return column >= table.getColumnCount() || column < 0;
}

private MouseEvent createMouseEvent(MouseEvent e, int eventID) {
    return new MouseEvent((Component) e.getSource(), eventID, e.getWhen(), e.getModifiers(), e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger(), e.getButton());
}
private void save(TableCell cell) {
    oldTableCell = cell;
}

private void saveComponent(JComponent component) {
    oldComponent = component;
}}


public class TableCell {

public int row;
public int column;

public TableCell() {
}

public TableCell(int row, int column) {
    this.row = row;
    this.column = column;
}

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final TableCell other = (TableCell) obj;
    if (this.row != other.row) {
        return false;
    }
    if (this.column != other.column) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
    int hash = 7;
    hash = 67 * hash + this.row;
    hash = 67 * hash + this.column;
    return hash;
}}

public class ActiveJComponentTableCellRenderer<T extends JComponent> extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {

private Map<TableCell, T> components;
private JComponentFactory<T> factory;

public ActiveJComponentTableCellRenderer() {
    this.components = new HashMap<TableCell, T>();        
}

public ActiveJComponentTableCellRenderer(JComponentFactory<T> factory) {
    this();
    this.factory = factory;
}

public T getComponent(TableCell key) {
    T component = components.get(key);
    if (component == null && factory != null) {
        // lazy-load component
        component = factory.build();
        initialiseComponent(component);
        components.put(key, component);
    }
    return component;
}

/**
 * Override this method to provide custom component initialisation code
 * @param component passed in component from getComponent(cell)
 */
protected void initialiseComponent(T component) {
}

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    return getComponent(new TableCell(row, column));
}

@Override
public Object getCellEditorValue() {
    return null;
}

@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    return getComponent(new TableCell(row, column));
}

public void setComponentFactory(JComponentFactory factory) {
    this.factory = factory;
}}

public interface JComponentFactory<T extends JComponent> {
T build();
}

要使用它,您需要将监听器注册为表格上的鼠标和运动监听器,并在适当的单元格上注册渲染器。如果你想拦截 actionPerformed 类型的事件,像这样覆盖 ActiveJComponentTableCellRenderer.initialiseComponent() :

protected void initialiseComponent(T component) {
    component.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            stopCellEditing();
        }
    });
}

关于java - JTable 中 JEditorPane 中的超链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1203734/

相关文章:

java - Android - 找不到 NullPointerException 的原因

Java 8 - 使用 Comparator 以不同顺序比较多个字段

javascript - 如何找出表单中的哪个文本区域处于焦点状态?

java - 如何使用 JFreeChart 固定自动范围避免负值

Java ProgressBar 在下载内容时添加值

java - HTTP 状态 500 - 未找到 javax.servlet.ServletException 文件

java - 对作为一个类实例的字段使用 Mock

javascript - 选择文本时如何停止浏览器滚动?潜在的浏览器错误?

html - 将元素固定在粘性元素下方

java - 矩阵到 JTable