JTable with close button in column header

I am trying to create a table with custom column headers. I want the column headings to include a button that users can click on. The function of the button will be to remove the column from the table. Essentially, I'm trying to create something like this .

Here is my code:

public class CustomColumnHeadersTable { private static String[] columnNames = { "Column 1", "Column 2", "Column 3" }; private static String[][] data = { {"A", "B", "C"}, {"D", "E", "F"}, {"G", "H", "I"} }; public CustomColumnHeadersTable() { DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames); JTable table = new JTable(model); JScrollPane scrollPane = new JScrollPane(table); //set Header Renderer of each column to use the Custom renderer Enumeration enumeration = table.getColumnModel().getColumns(); while (enumeration.hasMoreElements()) { TableColumn aColumn = (TableColumn) enumeration.nextElement(); aColumn.setHeaderRenderer(new CustomColumnCellRenderer()); } JFrame frame = new JFrame(); frame.getContentPane().add(scrollPane, BorderLayout.CENTER); frame.setPreferredSize(new Dimension(300, 150)); frame.pack(); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } public static void main(String[] args) { CustomColumnHeadersTable ccht = new CustomColumnHeadersTable(); } } class CustomColumnCellRenderer implements TableCellRenderer { private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif"; //using a URL for the icon, so I don't have to upload the icon with the question private static Dimension buttonSize = new Dimension(16, 16); private static Dimension buttonBoxSize = new Dimension(16, 16); private static Border panelBorder = BorderFactory.createRaisedBevelBorder(); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JPanel panel = new JPanel(); JLabel label = new JLabel(); JButton button = new JButton(); Box buttonBox = Box.createHorizontalBox(); BorderLayout layout = new BorderLayout(); label.setText(table.getColumnName(column)); try { button.setIcon(new ImageIcon(new URL(iconURL))); } catch (MalformedURLException ex) { Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex); } //set size of the button and it box button.setMaximumSize(buttonSize); button.setSize(buttonSize); button.setPreferredSize(buttonSize); buttonBox.setMaximumSize(buttonBoxSize); buttonBox.setSize(buttonBoxSize); buttonBox.setPreferredSize(buttonBoxSize); button.addMouseListener(new CustomMouseListener()); //doesn't work... buttonBox.add(button); panel.add(label, BorderLayout.CENTER); panel.add(buttonBox, BorderLayout.EAST); panel.setBorder(panelBorder); return panel; } } class CustomMouseListener implements MouseListener { public void mouseClicked(MouseEvent e) { System.out.println("Mouse Clicked."); } public void mousePressed(MouseEvent e) { System.out.println("Mouse Pressed."); } public void mouseReleased(MouseEvent e) { System.out.println("Mouse Released."); } public void mouseEntered(MouseEvent e) { System.out.println("Mouse Entered."); } public void mouseExited(MouseEvent e) { System.out.println("Mouse Exited."); } } 

By default, if I understand correctly, JTable uses JLabel to display column headers. My idea is to use a custom implementation of TableCellRenderer and create my own column header from several components, namely JPanel, which contains JLabel and JButton. I create and return this to the getTableCellRendererComponent (...) function.

Visually it works. The problem is that I cannot detect mouse clicks on the button (or, for that matter, on the panel that holds it). Just adding a MouseListener to a button does not work. The event never came.

I found several similar things on the Internet, but they do not achieve the functionality I need.

First, there is an example of how to put a JCheckBox in the header, here:

 http://java-swing-tips.blogspot.com/2009/02/jtableheader-checkbox.html 

The problem is that the whole title is a checkbox. When you click on the checkbox or on the corresponding label, the same effect is created. Therefore, sorting a column is not possible. I would like to have a click on the label to sort the column, and clicking on the close button will remove the column from the table. In other words, the header should have two separate areas with separate mouse event handlers.

I found another example here:

 http://www.devx.com/getHelpOn/10MinuteSolution/20425/1954?pf=true 

This includes placing JButtons in the cells of the table, and then detecting mouse clicks in the table itself, calculating the column and row where the click occurred, and sending the event to the corresponding button.

There are several issues with this. First, the buttons are in the cells, not in the headers. Secondly, this is again one component, and not several components inside JPanel. Although I got the idea of โ€‹โ€‹dispatching events from this example, I cannot get it to work for a composite component.

I tried a different approach. I suggested that if I can get the coordinates of the close buttons, then, knowing the coordinates of the mouse click, I can figure out which button to click and send the event accordingly. I conducted several tests and found that the components inside the table header are not actually located on the screen.

I added the JButton static variable to my main (public) class and created a class that implements TableCellRenderer as an inner class of the main class. In getTableCellRendererComponent (...), before returning, I assign the JButton that I just created for this static variable. That way, I can talk to him, so to speak. Then, basically, I tried to use getX (), getY (), getWidth (), getHeight () and getLocationOnScreen () using this static variable. X, Y, Width and Height all return 0. GetLocationOnScreen () crashes the program, saying that the component must be present on the screen for this function to work.

The code for this looks something like this:

  public class CustomColumnHeadersTable { private static JButton static_button; ///the rest as before.... 

"the CustomColumnCellRenderer class implements the TableCellRenderer" becomes the inner class of the CustomColumnHeadersTable. To do this, I have to discard the static variables in CustomColumnCellRenderer, so I was not worried about icons or URLs or anything like that. Instead of a button with an icon, I just used a simple button that said "BUTTON" ...

Next, inside getTableCellRendererComponent (...), immediately before the return statement, I did

 static_button = button; 

And finally, inside main (), I tried to do this:

  System.out.println("X: " + static_button.getX()); System.out.println("Y: " + static_button.getY()); System.out.println("W: " + static_button.getWidth()); System.out.println("H: " + static_button.getHeight()); System.out.println("LOC: " + static_button.getLocation()); System.out.println("LOC on screen: " + static_button.getLocationOnScreen()); 

The result is as follows:

 X: 0 Y: 0 W: 0 H: 0 LOC: java.awt.Point[x=0,y=0] Exception in thread "main" java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location at java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:1943) at java.awt.Component.getLocationOnScreen(Component.java:1917) ... 

In other words, the button has all sizes 0, and according to Java, it is actually not on the screen (although I see it ...). Calling the getLocationOnScreen () function causes the program to crash.

So please help if you can. Maybe someone knows how to do this. Maybe you could suggest a different approach to try. Or maybe you know that this is not possible at all ...

Thank you for your help.

+8
java swing jbutton jtable tablecellrenderer
source share
3 answers

Yes, I was surprised the first time when I tried to do something like this, for example, putting JButton in a JList - it does not quite work for the reasons you indicated. The right way to do what I wanted to use list-like LayoutManager was to put JButton on the list.

In your case, I would try to use a combination of MouseListeners and MouseListeners , which I believe will work correctly. Using JLabel as a title allows you to provide both an image and text, and you can register MouseListener with MouseListener . Of course, you will not get the same visual effects as if you click on the JButton button (i.e. the button is pressed), but it will allow you to use the same functions.

Another thing you need to pay attention to is using a static JButton (or any JComponent ). The problem with this is that the JButton coordinates are stored in JButton itself, which means that you cannot use the same JButton in different places. In other words, if you try to add the same static JButton to the list several times, you will not see several JButton s, you will only see JButton in the place where you last added it. The correct way to achieve the desired effect is to instead statically refer to the contents of the JButton , namely the image and text, instead of the JButton itself. Then just create a new JButton using the image and text and place it where you want. In general, it is best to avoid storing static references to Swing components, since this makes it impossible to have multiple instances of the same component.

Swings sometimes do not swing. Hope I have provided some useful information - keep fighting the good fight!

+2
source share

You can use JXTable (from the swingx palette) that have this option to โ€œdisableโ€ a particular column from your table.

Andrey Ionut Apopey

+1
source share

I got a solution:

 public class CustomColumnHeadersTable { public static Vector<JButton> buttons = new Vector<JButton>(); private static String[] columnNames = { "Column 1", "Column 2", "Column 3" }; private static String[][] data = { {"A", "B", "C"}, {"D", "E", "F"}, {"G", "H", "I"} }; class ColumnHeaderListener extends MouseAdapter { public void mouseClicked(MouseEvent evt) { JTable table = ((JTableHeader) evt.getSource()).getTable(); TableColumnModel colModel = table.getColumnModel(); int index = colModel.getColumnIndexAtX(evt.getX()); if (index == -1) { return; } Rectangle headerRect = table.getTableHeader().getHeaderRect(index); if( 1== index){ if(headerRect.contains(evt.getX() , evt.getY())){ int xx = evt.getX() - headerRect.x ; for (int i = 0; i < buttons.size(); i++) { JButton button = buttons.get(i); Rectangle re = button.getBounds(); if(re.contains( xx, evt.getY())){ System.out.println("Bingle"); } } } } } } public CustomColumnHeadersTable() { DefaultTableModel model = new DefaultTableModel((Object[][]) data, columnNames); JTable table = new JTable(model); JScrollPane scrollPane = new JScrollPane(table); JTableHeader header = table.getTableHeader(); header.addMouseListener(new ColumnHeaderListener()); //set Header Renderer of each column to use the Custom renderer Enumeration enumeration = table.getColumnModel().getColumns(); while (enumeration.hasMoreElements()) { TableColumn aColumn = (TableColumn) enumeration.nextElement(); aColumn.setHeaderRenderer(new CustomColumnCellRenderer(buttons)); } JFrame frame = new JFrame(); frame.getContentPane().add(scrollPane, BorderLayout.CENTER); frame.setPreferredSize(new Dimension(300, 150)); frame.pack(); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } public static void main(String[] args) { CustomColumnHeadersTable ccht = new CustomColumnHeadersTable(); } } class CustomColumnCellRenderer implements TableCellRenderer { private static String iconURL = "http://www.accessdubuque.com/images/close_icon.gif"; //using a URL for the icon, so I don't have to upload the icon with the question private static Dimension buttonSize = new Dimension(16, 16); private static Dimension buttonBoxSize = new Dimension(16, 16); private static Border panelBorder = BorderFactory.createRaisedBevelBorder(); Vector<JButton> buttons = null; JPanel panel = new JPanel(); JLabel label = new JLabel(); JButton button = new JButton(); CustomColumnCellRenderer( Vector<JButton> buttons) { this.buttons = buttons; try { button.setIcon(new ImageIcon(new URL(iconURL))); } catch (MalformedURLException ex) { Logger.getLogger(CustomColumnCellRenderer.class.getName()).log(Level.SEVERE, null, ex); } //set size of the button and it box button.setMaximumSize(buttonSize); button.setSize(buttonSize); button.setPreferredSize(buttonSize); BorderLayout layout = new BorderLayout(); panel.setLayout(layout); panel.add(label, BorderLayout.CENTER); panel.add(button, BorderLayout.EAST); panel.setBorder(panelBorder); buttons.add(button); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { label.setText(table.getColumnName(column)); return panel; } } 
0
source share

All Articles