Making a JButton clickable inside a JTable

Here is the screenshot of what I want to do :

enter image description here

What's happening there is the JButton shows correctly but nothing happens when I click on it. After some search, I've found that the Object returned by table.getValueAt() is a String instead of a JButton...

Here is the code :

tblResult = new JTable(data,cols) {
    public TableCellRenderer getCellRenderer( int row, int column ) {
        return new ClientsTableRenderer();
    }
};

I use this for populating at run-time the JTable : (tblResult is now Clients.rblResult)

SwingUtilities.invokeLater( new Runnable() {
    public void run() { 

        DefaultTableModel aModel = new DefaultTableModel() {
            //setting the jtable read only
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }               
        };


    String[] cols = {"N°","Société", "TVA", "CP", "Ville", ""};
    aModel.setColumnIdentifiers(cols);

    Object[] temp = new Object[6];
    for(int i=0;i<result.length;i++) {

        temp[0] = result[i].custNumber;
        temp[1] = result[i].name;
        temp[2] = result[i].tva;
        temp[3] = result[i].cp;
        temp[4] = result[i].city;
        temp[5] = "Consulter";

        aModel.addRow(temp);

    }

    Clients.tblResult.setModel(aModel);

    Clients.tblResult.addMouseListener(new JTableButtonMouseListener(Clients.tblResult));
    }}  
); 

Here the ClientsTableRenderer class

public class ClientsTableRenderer extends JPanel implements TableCellRenderer {
    @Override
    public Component getTableCellRendererComponent( final JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        setBackground(Color.WHITE);
        if(column < 5) {
            JLabel label =  new JLabel(value.toString());
            JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER,0,9));
            panel.setBackground(Color.WHITE);
            panel.add(label);
            this.add( panel);
        } else {

            JButton button = new JButton(value.toString());
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent arg0) {
                    System.out.println("Clicked !");
                }
            });
            JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER,0,3));
            panel.setBackground(Color.WHITE);
            panel.add(button);
            this.add(panel);
        }


        return this;
    }


}

And finaly, the JTableButtonMouseListener() :

public class JTableButtonMouseListener extends MouseAdapter {
      private final JTable table;

      public JTableButtonMouseListener(JTable table) {
        this.table = table;
      }

      @Override public void mouseClicked(MouseEvent e) {
        int column = table.getColumnModel().getColumnIndexAtX(e.getX());
        int row    = e.getY()/table.getRowHeight(); 
        System.out.println("Col :"+column + "row:"+row);

        if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() && column >= 0) {
          Object value = table.getValueAt(row, column);
          System.out.println("Value :"+value.getClass().getName());
          if (value instanceof JButton) {
            ((JButton)value).doClick();
          }

        }
      }
    }

I'm kindly new to Java, help would be very much appreciated :)

Thanks in advance !


Solution 1:

This Table Button Column from Rob Camick may fit your needs.

Solution 2:

The problem is that the JButton no longer exists when painted in the table. Those components are only used to create a 'stamp' when the table is rendered. There is no actual button present.

There is a way to allow you to click on the button, and still keep your table non-editable, but it is far from proper code. Just a quick outline for a possible solution (I do not have the time at this moment to give a full code example)

  • attach a mouse listener to the table
  • when you receive a mouse click, determine the cell in which the mouse click occurred
  • ask the table renderer for the component for that cell
  • use the location of the mouse click to determine whether a button is present in the component from the previous step at that particular location
  • if so, do the click through the button api (the doClick method)

And this is not even the dirty part of the code. Since your renderer (hopefully) does not return a new JButton each time, you should in your ActionListener which is attached to the JButton keep track of for which component the click actually occurred. A possible solution is to keep a reference to the table model value for which you the last time created a JButton (so in the getCellRendererComponent method keep track of the row/column), but I am unsure whether this is the best approach.

As said, a possible solution but far from elegant.

The easiest way is to just make that one column editable and use an editor, as pointed out in other answers