2012-04-27 2 views
20

Voici la capture d'écran de ce que je veux faire:Faire cliquable JButton dans un JTable

enter image description here

Qu'est-ce qui se passe, il y a la DButton montre bien, mais rien ne se passe quand je clique dessus. Après quelques recherches, j'ai trouvé que le Object retourné par table.getValueAt() est une chaîne au lieu d'un JButton ...

Voici le code:

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

J'utilise cela pour peuplant lors de l'exécution le JTable: (tblResult est maintenant 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)); 
    }} 
); 

ici, la classe ClientsTableRenderer

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


} 

Et finalement, le 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(); 
      } 

     } 
     } 
    } 

Je suis gentiment nouveau à Java, aide serait très appréciée :)

Merci à l'avance!

+0

valeur.getClass()? Il renvoie également "java.lang.String" – noli

+0

CellRenderer sont uniquement utilisés pour "peindre" la table, ils ne sont pas définis comme "composants Live". getValueAt renvoie une valeur du TableModel, pas un composant, donc il retournera probablement "Consulter" –

+0

C'est ce que je pensais. Cependant, y a-t-il un moyen de le réparer? – noli

Répondre

9

Le problème est que le JButton n'existe plus lorsque peint dans le tableau. Ces composants sont uniquement utilisés pour créer un 'tampon' lorsque la table est rendue. Il n'y a pas de bouton réel présent.

Il existe un moyen de vous permettre de cliquer sur le bouton, et gardez toujours votre table non modifiable, mais il est loin du bon code. Juste un aperçu rapide d'une solution possible (je n'ai pas le temps en ce moment pour donner un exemple complet de code)

  • attacher un écouteur de souris à la table
  • lorsque vous recevez un clic de souris, déterminer la cellule dans laquelle le clic de la souris est produite
  • demander la table de rendu pour le composant pour cette cellule
  • utiliser l'emplacement du clic de la souris afin de déterminer si un bouton est présent dans le composant de l'étape précédente à cet endroit particulier
  • si oui, faites le clic à travers le bouton api (la méthode doClick)

Et ce n'est même pas la partie sale du code. Depuis votre moteur de rendu (heureusement) ne retourne pas un nouveau JButton à chaque fois, vous devriez dans votre ActionListener qui est attaché à la JButton garder la trace de pour quel composant le clic s'est réellement produit. Une solution possible est de garder une référence à la valeur du modèle de table pour laquelle vous avez créé un JButton (donc dans la méthode getCellRendererComponent garder trace de la ligne/colonne), mais je ne sais pas si c'est la meilleure approche. Comme dit, une solution possible mais loin d'être élégante.

La façon la plus simple est de simplement faire qu'une colonne modifiable et utiliser un éditeur, comme l'a souligné dans d'autres réponses

+0

Merci pour votre temps. eh bien, ce n'est pas aussi sexy que prévu mais ça pourrait être une solution. – noli

+0

Je pourrais essayer d'ajouter du code si je trouve du temps pendant le week-end. Je sais que la solution ci-dessus fonctionne pour un 'JTree' car je l'ai utilisé là – Robin

7

Essayez ceci:

import java.awt.Color; 
import java.awt.Component; 
import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.DefaultCellEditor; 
import javax.swing.JButton; 
import javax.swing.JCheckBox; 
import javax.swing.JFrame; 
import javax.swing.JOptionPane; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.UIManager; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableCellRenderer; 
import javax.swing.table.TableModel; 

public class TableWithButtonDemo 
{ 
    private JFrame frame = new JFrame("Table Demo"); 
    private String[] columnNames = { "String", "Integer", "Float", "" }; 
    private Object[][] data = { { "Dummy", new Integer(12), new Float(12.15), "Consulter" } }; 
    private TableModel model = new DefaultTableModel(data, columnNames) 
    { 
    private static final long serialVersionUID = 1L; 

    public boolean isCellEditable(int row, int column) 
    { 
     return column == 3; 
    } 
    }; 
    private JTable table = new JTable(model); 

    public TableWithButtonDemo() 
    { 
    table.getColumnModel().getColumn(3).setCellRenderer(new ClientsTableButtonRenderer()); 
    table.getColumnModel().getColumn(3).setCellEditor(new ClientsTableRenderer(new JCheckBox())); 
    table.setPreferredScrollableViewportSize(table.getPreferredSize()); 
    table.setShowHorizontalLines(true); 
    table.setShowVerticalLines(false); 

    JScrollPane scroll = new JScrollPane(table); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.add(scroll); 
    frame.pack(); 
    frame.setLocation(150, 150); 
    frame.setVisible(true); 
    } 

    public static void main(String[] args) throws Exception 
    { 
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
    EventQueue.invokeLater(new Runnable() 
    { 
     public void run() 
     { 
     new TableWithButtonDemo(); 
     } 
    }); 
    } 

    class ClientsTableButtonRenderer extends JButton implements TableCellRenderer 
    { 
    public ClientsTableButtonRenderer() 
    { 
     setOpaque(true); 
    } 

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) 
    { 
     setForeground(Color.black); 
     setBackground(UIManager.getColor("Button.background")); 
     setText((value == null) ? "" : value.toString()); 
     return this; 
    } 
    } 
    public class ClientsTableRenderer extends DefaultCellEditor 
    { 
    private JButton button; 
    private String label; 
    private boolean clicked; 
    private int row, col; 
    private JTable table; 

    public ClientsTableRenderer(JCheckBox checkBox) 
    { 
     super(checkBox); 
     button = new JButton(); 
     button.setOpaque(true); 
     button.addActionListener(new ActionListener() 
     { 
     public void actionPerformed(ActionEvent e) 
     { 
      fireEditingStopped(); 
     } 
     }); 
    } 
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) 
    { 
     this.table = table; 
     this.row = row; 
     this.col = column; 

     button.setForeground(Color.black); 
     button.setBackground(UIManager.getColor("Button.background")); 
     label = (value == null) ? "" : value.toString(); 
     button.setText(label); 
     clicked = true; 
     return button; 
    } 
    public Object getCellEditorValue() 
    { 
     if (clicked) 
     { 
     JOptionPane.showMessageDialog(button, "Column with Value: "+table.getValueAt(row, 1) + " - Clicked!"); 
     } 
     clicked = false; 
     return new String(label); 
    } 

    public boolean stopCellEditing() 
    { 
     clicked = false; 
     return super.stopCellEditing(); 
    } 

    protected void fireEditingStopped() 
    { 
     super.fireEditingStopped(); 
    } 
    } 

} 
+0

J'ai essayé cela et fonctionne facilement – Mikel

0

surcharge votre Modèle de table, et set isCellEditable (int, int) renvoie false pour les cellules avec des boutons.

Cela fonctionne très bien avec un MouseListener ajouté à la table.

0

Voici ma solution

ButtonEditor.java

public abstract class ButtonEditor extends DefaultCellEditor implements ActionListener { 
private static final long serialVersionUID = 1L; 

/** The cell's row. */ 
protected int row; 

/** The cell's column. */ 
protected int column; 

/** The cell's column. */ 
protected JTable table; 

/** The button we are editing. */ 
protected JButton button; 

/** The panel used when editing. */ 
protected JPanel panel = new JPanel(new GridBagLayout()); 

/** Constructor */ 
public ButtonEditor() {super(new JCheckBox());} 

/** 
* This method is called when the user try to modify a cell. 
* In this case it will be called whenever the user click on the cell. 
* @param table 
* @param value 
* @param isSelected 
* @param row 
* @param column 
* @return JPanel The JPanel returned contains a JButton with an ActionListener. 
*/ 
@Override 
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 
    this.row = row; 
    this.column = column; 
    this.table = table; 
    button = (JButton) value; 

    //prevent to add the action listener everytime the user click on the cell. 
    if(button.getActionListeners().length == 0) button.addActionListener(this); 

    panel.add(button); 
    panel.setBackground(table.getGridColor()); 
    return panel; 
} 

/** 
* Return a renderer for JButtons. The result is a button centered in the table's cell. 
* @return 
*/ 
public static TableCellRenderer getRenderer() { 
    return new TableCellRenderer() { 
     @Override 
     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
      JPanel panel = new JPanel(new GridBagLayout()); 
      panel.add((JButton) value); 
      panel.setBackground(table.getGridColor()); 
      return panel; 
     } 
    }; 
} 

}

Et voici comment l'utiliser:

Demo.java

table.setDefaultRenderer(JButton.class, ButtonEditor.getRenderer()); 
    table.setDefaultEditor(JButton.class, new ButtonEditor() { 
     @Override 
     public void actionPerformed(ActionEvent e) { 

      //handle clicks here. for example: 
      if(column == 5) { 
       System.out.Println(row); 
       button.setFocusPainted(false);     
      } 
     } 
    });