突出显示JTable的列标题

我正在构建一个小JTable,并且想要突出显示列标题(和行标题 – 行标题部分实际上正在工作),当选择单元格时,可以更容易地找到与此单元格相关联的名称。 这是一张图片:

在此处输入图像描述

我已经尝试使用以下方法切换标题的渲染器:

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

但是只有当我点击标题并且总是说isSelected为false时才会调用它。

这是我用于行名的代码,包括渲染器内的高亮 – 代码不是由我,我只是修改了一点:

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

在这里,我们有相关的部分来创建表:

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

类costTableModel,只是为了完整性:

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

提前谢谢你的帮助!

我遇到的基本问题是表头与选择更改之间没有任何关联。 事实上,标题是非常聪明的重绘…

我最终提供了自己的标题,它将一个监听器附加到表的选择模型,并重新绘制了更改选择的标题。

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

一个轻微的变体:当我阅读问题时,主要问题是标题没有更新选择更改。 使用自定义标头侦听选择更改对该方案没有多大帮助。

实际上,JTableHeader已经监听ColumnModel,而模型的更改通知包括选择更改。 故意实现columnSelectionChange方法不执行任何操作:

 // --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. 

一个自定义标题可以简单地实现重绘(这里懒得我在表的工厂方法中做它只是为了让我无法连接到表,你可以轻松地使它成为一个独立的类:-)。

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

还使用表api调整了Mad的渲染器:

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

至于观察:

总是说isSelected是假的

原因是BasicTableHeaderUI略有怪异:

 ui.selected != columnModel.selected 

uiSelected是键绑定可访问的列 – 如果laf支持它并且标题是focusOwner。 对我来说真的没有意义,但完全定义ui和columnModel选择的语义陷入了对新宝贝fx的兴奋,这是被遗忘的;-)