How can I put a control in the JTableHeader of a JTable?
There are two parts of the problem (as I see it :-)
Usability: inventing UI-interaction/elements is prone to confusing users. In no particular order:
- the column header title is meant to describe the content of the column, that content description is lost when in replacing it with an action description
- it's not immediately (for me, the dumbest user on earth :-) clear that the header cell has the function of a toggle button. Accidentally clicking it will loose all earlier content state in that column
So even if interaction analysis comes out with a clear we-do-need/want-it,
- action only in-addition to the content
- use a widget that's clearer (e.g. a tri-state checkbox all-de-/selected, mixed content). Also, de-/selecting must both be possible from mixed content. On second thought, a checkbox probably isn't the best choice either, didn't dig further
- minimize the possibility to accidentally (just for me :-) change bulk state, (e.g. by a clear visual separation of an active area - the checkbox icon) from the "normal header" region.
Technical aspects
- TableHeader is not designed for "live" components. Whatever is wanted has to be controlled by ourselves
- examples are around (e.g. JIDE grid supports adding components)
- fiddling with header tends to look unattractive because it's not trivial to change the renderer and at the same time keep the LAF provided appearance
Can I add a button to a JTable column header?
Here is an example where someone wanted to do the same thing.
Adding JButton to the JTable
Yes, it is possible. You can simply add the button to the table header. The only thing to know is that JTableHeader does not have a layout, so you need to set one.
Here is a simple demo code of this:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
public class TestTable2 {
protected void initUI() {
DefaultTableModel model = new DefaultTableModel();
for (int i = 0; i < 5; i++) {
model.addColumn("Col-" + (i + 1));
}
for (int i = 0; i < 200; i++) {
Vector<Object> row = new Vector<Object>();
for (int j = 0; j < 5; j++) {
row.add("New cell - " + (j + 1));
}
model.addRow(row);
}
JTable table = new JTable(model);
final JButton button = new JButton("Click me");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(button, "You have clicked me");
}
});
JTableHeader header = table.getTableHeader();
header.setLayout(new FlowLayout(FlowLayout.TRAILING, 5, 0));
header.add(button);
JFrame frame = new JFrame(TestTable2.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollpane = new JScrollPane(table);
frame.add(scrollpane, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestTable2().initUI();
}
});
}
}
JTable Right Align Header
Here's an alternate approach to modifying the TableCellRenderer
of a table's JTableHeader
. It's not strictly necessary for this usage, but it minimizes the impact on the UI delegate's appearance.
Typical usage:
JTable table = new JTable(…);
JTableHeader header = table.getTableHeader();
header.setDefaultRenderer(new HeaderRenderer(table));
Custom header renderer:
private static class HeaderRenderer implements TableCellRenderer {
DefaultTableCellRenderer renderer;
public HeaderRenderer(JTable table) {
renderer = (DefaultTableCellRenderer)
table.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col) {
return renderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, col);
}
}
Related Topics
Why Do This() and Super() Have to Be the First Statement in a Constructor
Recursively List Files in Java
Spring Boot Configure and Use Two Data Sources
What Is the Equivalent of the C++ Pair≪L,R≫ in Java
Split String to Equal Length Substrings in Java
Real Differences Between "Java -Server" and "Java -Client"
Placing Component on Glass Pane
When to Use: Java 8+ Interface Default Method, Vs. Abstract Method
Why Aren't Java Collections Remove Methods Generic
When Should I Use "This" in a Class
How to List the Files Inside a Jar File
Java Process With Input/Output Stream
Converting 'Arraylist≪String≫ to 'String[]' in Java
Get Os-Level System Information
Why Is Volatile Used in Double Checked Locking
How to Tell Jackson to Ignore a Field During Serialization If Its Value Is Null
What Are Java Command Line Options to Set to Allow Jvm to Be Remotely Debugged