Pregunta

Te lo juro...espero que esta es la última pregunta que tengo para preguntar como esta, pero estoy a punto de volverse loco.

Tengo un JTable utilizando una costumbre TableCellRenderer que utiliza un JEditorPane para mostrar html en las células individuales de la JTable.¿Cómo puedo proceso haciendo clic en los enlaces que se muestran en el JEditorPane?

Yo sé acerca de HyperlinkListener pero no los eventos de ratón se consigue a través de la JTable a la EditorPane para cualquier HyperlinkEvents para ser procesados.

¿Cómo puedo proceso de Hipervínculos en un JEditorPane dentro de un JTable?

¿Fue útil?

Solución

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

Aquí hay algunas clases que creé en el pasado para permitir que JButton roll-over funcione en un JTable, pero también debería poder reutilizar la mayor parte de esto para su problema. Tenía un JButton separado para cada celda que lo requería. Con eso, este ActiveJComponentTableMouseListener determina en qué celda ocurre el evento del mouse y envía un evento al componente correspondiente. Es el trabajo de ActiveJComponentTableCellRenderer para 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 se acumulan eventos redundantes. Implementar esto para el hipertexto no debería ser tan diferente, y es posible que usted también quiera relanzarlo. 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 al oyente como mouse y detector de movimiento en la tabla, y registrar el renderizador en las celdas apropiadas. Si desea interceptar eventos de tipo ActionPerformed, anule ActiveJComponentTableCellRenderer.initialiseComponent () de esta manera:

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

Otros consejos

Si registra un MouseListener en la 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 () . Luego pasa ese Point a JTable del rowAtPoint (pt) y columnAtPoint (pt) . Desde allí, puede obtener el texto a través de JTable.getValueAt (fila, columna) . Ahora tienes el valor de tu celda, por lo que puedes determinar si es un enlace o no y hacer lo que quieras con el resultado.

Para resolver el mismo problema, en lugar de intentar que JEditorPane produzca el evento, procesé el MouseEvent producido por JTable e hice que el oyente descubriera cuándo estábamos haciendo clic en un enlace o no.

Aquí está el código.Mantiene un mapa de JEditorPanes, así que asegúrese de no tener pérdidas de memoria y de borrar este mapa adecuadamente si los datos de la tabla pueden cambiar.Está ligeramente modificado con respecto al código que realmente usé: en la versión que realmente usé, solo producía JEditorPane cuando en realidad los enlaces estaban en el html, y no se molestaba con JEditorPanes cuando no existían dichos 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
            }
        }
    }
};
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top