nombre encabezado columnas agregar java swing jtable highlight jtableheader

columnas - encabezado jtable java



Resaltando un encabezado de columna de una JTable (2)

Actualmente estoy creando un poco de JTable y quiero resaltar el encabezado de la columna (y los encabezados de las filas, la parte del encabezado de la fila está realmente funcionando) cuando se selecciona una celda para que sea más fácil encontrar los nombres asociados con esta celda. Aquí hay una foto:

Ya intenté desconectar el procesador del encabezado con esto:

table.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer());

Pero solo se llama cuando hago clic en el encabezado y siempre dice isSelected es falso.

Este es el código que uso para los nombres de las filas, incluido el resaltado dentro del representador; el código no es mío, simplemente lo modifiqué un poco:

/* * Use a JTable as a renderer for row numbers of a given main table. * This table must be added to the row header of the scrollpane that * contains the main table. */ public class RowNameTable extends JTable implements ChangeListener, PropertyChangeListener { private JTable main; public RowNameTable(JTable table) { main = table; main.addPropertyChangeListener(this); setFocusable(false); setAutoCreateColumnsFromModel(false); setModel(main.getModel()); setSelectionModel(main.getSelectionModel()); TableColumn column = new TableColumn(); column.setHeaderValue(" "); addColumn(column); column.setCellRenderer(new RowNameRenderer(main)); getColumnModel().getColumn(0).setPreferredWidth(table.getColumnModel().getColumn(0).getPreferredWidth()); setPreferredScrollableViewportSize(getPreferredSize()); } @Override public void addNotify() { super.addNotify(); Component c = getParent(); // Keep scrolling of the row table in sync with the main table. if (c instanceof JViewport) { JViewport viewport = (JViewport) c; viewport.addChangeListener(this); } } /* * Delegate method to main table */ @Override public int getRowCount() { return main.getRowCount(); } @Override public int getRowHeight(int row) { return main.getRowHeight(row); } /* * This table does not use any data from the main TableModel, * so just return a value based on the row parameter. */ @Override public Object getValueAt(int row, int column) { return Integer.toString(row + 1); } /* * Don''t edit data in the main TableModel by mistake */ @Override public boolean isCellEditable(int row, int column) { return false; } // // Implement the ChangeListener // public void stateChanged(ChangeEvent e) { // Keep the scrolling of the row table in sync with main table JViewport viewport = (JViewport) e.getSource(); JScrollPane scrollPane = (JScrollPane) viewport.getParent(); scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y); } // // Implement the PropertyChangeListener // public void propertyChange(PropertyChangeEvent e) { // Keep the row table in sync with the main table if ("selectionModel".equals(e.getPropertyName())) { setSelectionModel(main.getSelectionModel()); } if ("model".equals(e.getPropertyName())) { setModel(main.getModel()); } } /* * Borrow the renderer from JDK1.4.2 table header */ private static class RowNameRenderer extends DefaultTableCellRenderer { private JTable main; public RowNameRenderer(JTable main) { this.main = main; setHorizontalAlignment(JLabel.CENTER); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (table != null) { JTableHeader header = table.getTableHeader(); if (header != null) { setForeground(header.getForeground()); setBackground(header.getBackground()); setFont(header.getFont()); } } if (isSelected) { setFont(getFont().deriveFont(Font.BOLD)); } setText((value == null) ? "" : main.getColumnName(row)); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } } }

Y aquí tenemos la parte relevante para crear la tabla:

costTableModel = new CostTableModel(costCalc); table = new JTable(costTableModel); table.setPreferredScrollableViewportSize(table.getPreferredSize()); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); table.setCellSelectionEnabled(true); scrollPane = new JScrollPane(table); RowNameTable nameTable = new RowNameTable(table); scrollPane.setRowHeaderView(nameTable);

Y la clase costTableModel, solo por completitud:

public class CostTableModel extends AbstractTableModel { private CostCalculator costCalc; public CostTableModel(CostCalculator costCalc) { this.costCalc = costCalc; } @Override public int getRowCount() { return costCalc.getPersonsList().size(); } @Override public int getColumnCount() { return costCalc.getPersonsList().size(); } @Override public String getColumnName(int col) { return costCalc.getPersonsList().get(col).getName(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { Person debtor = costCalc.getPersonsList().get(rowIndex); Person debtee = costCalc.getPersonsList().get(columnIndex); return costCalc.getAmountOwed(debtor, debtee); } @Override public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } }

¡De forma anticipada, muchas gracias por su ayuda!


El problema básico que tuve fue que no había conexión entre el encabezado de la tabla y el cambio de selección. De hecho, el encabezado es realmente inteligente con sus pinturas ...

Terminé proporcionando mi propio encabezado, que adjuntó un oyente al modelo de selección de la tabla y repintado el encabezado en la selección modificada.

import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.EventQueue; import java.util.List; import javax.swing.Icon; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.RowSorter; import javax.swing.RowSorter.SortKey; import static javax.swing.SortOrder.ASCENDING; import static javax.swing.SortOrder.DESCENDING; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.JTableHeader; public class TestColumnHighlight { public static void main(String[] args) { new TestColumnHighlight(); } public TestColumnHighlight() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JTable table = new JTable(); DefaultTableModel model = new DefaultTableModel( new Object[]{"abc", "def", "ghi", "jkl"}, 0); model.addRow(new Object[]{0, 0, 0, 0}); model.addRow(new Object[]{0, 0, 0, 0}); model.addRow(new Object[]{0, 0, 0, 0}); model.addRow(new Object[]{0, 0, 0, 0}); model.addRow(new Object[]{0, 0, 0, 0}); table.setModel(model); table.setTableHeader(new CustomTableHeader(table)); table.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer()); JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new JScrollPane(table)); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class CustomTableHeader extends JTableHeader { public CustomTableHeader(JTable table) { super(); setColumnModel(table.getColumnModel()); table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { repaint(); } }); } @Override public void columnSelectionChanged(ListSelectionEvent e) { repaint(); } } public class ColumnHeaderRenderer extends DefaultTableHeaderCellRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { super.getTableCellRendererComponent(table, value, selected, focused, row, column); int selectedColumn = table.getSelectedColumn(); System.out.println("Selected " + selectedColumn + "-" + column); if (selectedColumn == column) { Color bg = table.getSelectionBackground(); setBackground(bg); setOpaque(true); } else { setOpaque(false); } return this; } } public class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer { public DefaultTableHeaderCellRenderer() { setHorizontalAlignment(CENTER); setHorizontalTextPosition(LEFT); setVerticalAlignment(BOTTOM); setOpaque(false); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); JTableHeader tableHeader = table.getTableHeader(); if (tableHeader != null) { setForeground(tableHeader.getForeground()); } setIcon(getIcon(table, column)); setBorder(UIManager.getBorder("TableHeader.cellBorder")); return this; } protected Icon getIcon(JTable table, int column) { SortKey sortKey = getSortKey(table, column); if (sortKey != null && table.convertColumnIndexToView(sortKey.getColumn()) == column) { switch (sortKey.getSortOrder()) { case ASCENDING: return UIManager.getIcon("Table.ascendingSortIcon"); case DESCENDING: return UIManager.getIcon("Table.descendingSortIcon"); } } return null; } protected SortKey getSortKey(JTable table, int column) { RowSorter rowSorter = table.getRowSorter(); if (rowSorter == null) { return null; } List sortedColumns = rowSorter.getSortKeys(); if (sortedColumns.size() > 0) { return (SortKey) sortedColumns.get(0); } return null; } } }


Una pequeña variante: cuando leo la pregunta, el problema principal es que el encabezado no se actualiza en el cambio de selección de columna . Tener un encabezado personalizado para escuchar los cambios de selección de fila no ayuda mucho para ese escenario.

De hecho, un JTableHeader ya está escuchando ColumnModel y la notificación de cambio del modelo incluye cambios de selección. Solo el método columnSelectionChange se implementa intencionalmente para no hacer nada:

// --Redrawing the header is slow in cell selection mode. // --Since header selection is ugly and it is always clear from the // --view which columns are selected, don''t redraw the header.

Un encabezado personalizado simplemente puede implementarse para volver a pintar (aquí, "perezoso" lo hace en el método de fábrica de la tabla solo para ahorrarme el cableado a la mesa, puede convertirlo fácilmente en una clase independiente :-).

final JTable table = new JTable(new AncientSwingTeam()) { @Override protected JTableHeader createDefaultTableHeader() { // subclassing to take advantage of super''s auto-wiring // as ColumnModelListener JTableHeader header = new JTableHeader(getColumnModel()) { @Override public void columnSelectionChanged(ListSelectionEvent e) { repaint(); } }; return header; } }; table.setCellSelectionEnabled(true); table.getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer());

También modificó un poco el renderizador de Mad, usando la API de la tabla:

/** * Slightly adjusted compared to @Mad * - use table''s selectionBackground * - use table''s isColumnSelected to decide on highlight */ public static class ColumnHeaderRenderer extends DefaultTableCellHeaderRenderer { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) { super.getTableCellRendererComponent(table, value, selected, focused, row, column); if (table.isColumnSelected(column)) { setBackground(table.getSelectionBackground()); } return this; } }

En cuanto a la observación:

siempre dice isSelected es falso

La razón es una pequeña peculiaridad en BasicTableHeaderUI:

ui.selected != columnModel.selected

uiSelected es la columna a la que se accederá mediante combinaciones de teclas, si el laf lo admite y el encabezado es focusOwner. Realmente no tiene sentido para mí, pero definir completamente la semántica de ui y la selección de columnModel cayó en la emoción sobre el nuevo babe fx, que se olvida ;-)