Выделение заголовка столбца 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();
}
}
Спасибо за вашу помощь заранее!
2 ответов
основная проблема, с которой я столкнулся, заключалась в отсутствии связи между заголовком таблицы и изменением выбора. На самом деле, заголовок действительно умный с его перекрасками...
в итоге я предоставил свой собственный заголовок, который прикрепил прослушиватель к модели выбора таблицы и перекрасил заголовок в измененном выборе.
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;
}
}
}
небольшой вариант: как я прочитал вопрос, основная проблема заключается в том, что заголовок не обновляется на колонки изменить выбор. Наличие пользовательского заголовка listen to строка изменения выбора не очень помогают для этого сценария.
на самом деле, JTableHeader уже is прослушивание 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.
пользовательский заголовок может просто реализовать перекраску (здесь lazy me делает это в Заводском методе таблицы, чтобы избавить меня от проводки к таблице, вы можете легко сделать его автономным классом: -).
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());
также немного изменил рендерер Mad, используя table api:
/**
* 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 является false
причина-небольшая причуда в BasicTableHeaderUI:
ui.selected != columnModel.selected
uiSelected-столбец, который будет доступен для Привязок клавиш-если laf поддерживает его, а заголовок-focusOwner. На самом деле не имеет смысла для меня, но полностью определяя семантику ui и columnmodel selection попал в волнение о новом babe fx, о котором забыли; -)