que open example java html swing jtable jeditorpane

open - que es un jeditorpane en java



hipervínculos en JEditorPane en una JTable (3)

Lo juro ... espero que esta sea la última pregunta que tengo que hacer de esta manera, pero estoy a punto de volverme loco.

Tengo una JTable que usa un TableCellRenderer personalizado que usa un JEditorPane para mostrar html en las celdas individuales de JTable. ¿Cómo procedo a hacer clic en los enlaces que se muestran en JEditorPane?

Sé de HyperlinkListener pero ningún evento de mouse pasa a través de JTable al EditorPane para que se procese ningún HyperlinkEvents.

¿Cómo se procesan los hipervínculos en un JEditorPane dentro de una JTable?


EditorPane no recibe ningún evento porque el componente devuelto por TableCellRenderer solo puede mostrar y no interceptar eventos, por lo que es prácticamente lo mismo que una imagen, sin ningún comportamiento permitido. Por lo tanto, incluso cuando los oyentes están registrados, el componente devuelto nunca está "al tanto" de ningún evento. La solución para esto es registrar un MouseListener en JTable e interceptar todos los eventos relevantes desde allí.

Aquí hay algunas clases que creé en el pasado para permitir que la transferencia de JButton funcione en una JTable, pero también debería ser capaz de volver a utilizar la mayor parte de esto para su problema. Tenía un JButton por cada célula que lo requería. Con eso, este ActiveJComponentTableMouseListener determina en qué celda se produce el evento del mouse y distribuye un evento al componente correspondiente. El trabajo de ActiveJComponentTableCellRenderer es realizar un seguimiento de los componentes a través de un Mapa.

Es lo suficientemente inteligente como para saber cuándo ya se han activado los eventos, por lo que no recibirá un retraso de los eventos redundantes. La implementación de esto para el hipertexto no debería ser tan diferente, y es posible que también desee que se transfiera. Aquí están las clases

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

Para usarlo, desea registrar el oyente como el mouse y el oyente de movimiento en la tabla, y registrar el procesador en las celdas apropiadas. Si desea interceptar eventos de tipo actionPerformed, anule ActiveJComponentTableCellRenderer.initialiseComponent () como sigue:

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


Si registra un MouseListener en JTable, puede obtener fácilmente el texto en el punto de clic del mouse. Esto se haría generando un objeto Point desde MouseEvent , usando event.getX() y event.getY() . A continuación, rowAtPoint(pt) ese Point a rowAtPoint(pt) y columnAtPoint(pt) . A partir de ahí, puede obtener el texto a través de JTable.getValueAt(row, column) . Ahora tiene el valor de su celda, por lo que puede determinar si es un enlace o no y hacer lo que quiera con el resultado.


Para resolver el mismo problema, en lugar de intentar que JEditorPane produzca el evento, en su lugar procesé el evento MouseEvent producido por JTable, hice que el oyente se diera cuenta cuando estábamos haciendo clic en un enlace o no.

Aquí está el código. Mantiene un mapa de JEditorPanes, por lo que debe asegurarse de no tener pérdidas de memoria, y de borrar este mapa de forma adecuada si los datos de la tabla pueden cambiar. Está ligeramente modificado desde el código que realmente utilicé: en la versión que usé realmente, solo producía JEditorPane cuando en realidad los enlaces estaban en el html, y no me molestaba con JEditorPanes cuando no existían tales enlaces ...

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 } } } }; }