2009-07-29 48 views
2

我发誓...我希望这是我必须要问的最后一个问题,但我即将发疯。JEditorPane在JTable中的超链接

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

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

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

+0

您是否创建了MouseListener并将其附加到JTable? – 2009-07-30 01:28:19

回答

6

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

以下是我在过去创建的一些类,允许JButton转换在JTable中工作,但您应该可以重复使用大部分此类问题以解决您的问题。我为每个需要它的单元都有一个单独的JButton。这样,这个ActiveJComponentTableMouseListener可以处理鼠标事件发生在哪个单元格中,并将事件分派给相应的组件。这是ActiveJComponentTableCellRenderer的工作,通过Map来跟踪组件。

它足够聪明,可以知道它何时已经触发了事件,因此您不会收到冗余事件的积压。对超文本实现这一点不应该有所不同,并且您可能仍然需要翻阅。下面是类

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(); 
     } 
    }); 
} 
+0

哇,谢谢!这看起来像可以工作。然而,在我的情况下,我的行高取决于单元格的内容。 getRow(MouseEvent e)中有没有使用table.rowAtPoint(e.getPoint())的原因? – Victor 2009-07-30 17:28:13

+0

我认为rowAtPoint(...)可以工作,并且避免假设行高有固定。我想我是在无意中做出这个假设的时候,我只是试图让它工作(我也花了很多时间试图弄清楚如何让JTable单元向底层组件发出事件,并且最终感到有些沮丧!)。让我知道你是否使用rowAtPoint来使用它。 – shredderts 2009-07-31 03:03:39

+0

确实这种方法传递鼠标事件...但它仍然没有激活超链接更新事件。 – Victor 2009-07-31 17:40:31

1

如果您在JTable上注册了MouseListener,您可以轻松地在鼠标点击处获取文本。这将通过使用event.getX()event.getY()MouseEvent生成Point对象来完成。然后,您将该Point传递到JTablerowAtPoint(pt)columnAtPoint(pt)。从那里,你可以通过JTable.getValueAt(row, column)获得文本。现在你已经掌握了你的单元格的价值,所以你可以确定它是否是一个链接,并根据结果做你喜欢的事情。

+0

这不会工作,因为会有链接混合文本...并可能在一个单元格中的多个链接。 – Victor 2009-07-30 05:46:14

0

为了解决同样的问题,而不是试图有JEditorPane中产生的事件,而不是我处理完JTable中产生的MouseEvent ,当听我们点击一​​个链接时,听众就知道了。

这是代码。它保存了JEditorPanes的地图,因此请确保没有内存泄漏,并且如果表中的数据可以更改,则可以适当地清除此映射。它从我实际使用的代码稍微修改 - 在我实际使用的版本中,它只在实际链接在html中时才生成JEditorPane,并且在没有此类链接时不会与JEditorPanes混淆...

public class MessageWithPossibleHtmlLinksRenderer extends DefaultTableCellRenderer { 

private final Map<Integer, JEditorPane> editorPanes = new HashMap<>(); 

public MessageWithPossibleHtmlLinksRenderer(JTable table) { 
    // register mouseAdapter to table for link-handling 
    table.addMouseMotionListener(this.mouseAdapter); 
    table.addMouseListener(this.mouseAdapter); 
} 

private JEditorPane getOrCreateEditorPane(int row, int col) { 
    final int key = combine(row, col); 
    JEditorPane jep = editorPanes.get(key); 
    if (jep == null) { 
     jep = new JEditorPane(); 
     jep.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); 
     jep.setContentType("text/html"); 
     jep.setEditable(false); 
     jep.setOpaque(true); 
     editorPanes.put(key, jep); 
    } 
    return jep; 
} 

private static int combine(int row, int col) { 
    return row * 10 + col; // works for up to 10 columns 
} 

@Override 
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
    // modify here if you want JEditorPane only when links exist 
    if (value instanceof String && ((String) value).startsWith("<html>")) { 
     final JEditorPane jep = getOrCreateEditorPane(row, column); 
     jep.setText((String) value); 
     // code to adjust row height 
     return jep; 
    } else { 
     return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); 
    } 
} 

private AttributeSet anchorAt(MouseEvent e) { 
    // figure out the JEditorPane we clicked on, or moved over, if any 
    final JTable table = (JTable) e.getSource(); 
    final Point p = e.getPoint(); 
    final int row = table.rowAtPoint(p); 
    final int col = table.columnAtPoint(p); 
    final int key = combine(row, col); 
    final JEditorPane pane = this.editorPanes.get(key); 
    if (pane == null) { 
     return null; 
    } 
    // figure out the exact link, if any 
    final Rectangle r = table.getCellRect(row, col, false); 
    final Point relativePoint = new Point((int) (p.getX() - r.x), (int) (p.getY() - r.y)); 
    pane.setSize(r.getSize()); // size the component to the size of the cell 
    final int pos = pane.viewToModel(relativePoint); 
    if (pos >= 0) { 
     final Document doc = pane.getDocument(); 
     if (doc instanceof HTMLDocument) { 
      final Element el = ((HTMLDocument) doc).getCharacterElement(pos); 
      return (AttributeSet) el.getAttributes().getAttribute(HTML.Tag.A); 
     } 
    } 
    return null; 
} 

private final MouseAdapter mouseAdapter = new MouseAdapter() { 
    @Override 
    public void mouseMoved(MouseEvent e) { 
     final AttributeSet anchor = anchorAt(e); 
     final Cursor cursor = anchor == null 
       ? Cursor.getDefaultCursor() 
       : Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); 
     final JTable table = (JTable) e.getSource(); 
     if (table.getCursor() != cursor) { 
      table.setCursor(cursor); 
     } 
    } 

    @Override 
    public void mouseClicked(MouseEvent e) { 
     if (! SwingUtilities.isLeftMouseButton(e)) { 
      return; 
     } 
     final AttributeSet anchor = anchorAt(e); 
     if (anchor != null) { 
      try { 
       String href = (String) anchor.getAttribute(HTML.Attribute.HREF); 
       Desktop.getDesktop().browse(new URL(href).toURI()); 
      } catch (Exception ex) { 
       // ignore 
      } 
     } 
    } 
}; 
}