JButton in JTable

Status
Nicht offen für weitere Antworten.

Leroy42

Top Contributor
Wie bekomme ich es hin, Buttons in Tabellen unterzubringen.

Ich erzeuge eine JTable anhand von Datensätzen einer DB (Kunden, Artikel, ...)
und möchte je Zeile einen Button "Bearbeiten" einfügen.

Im Tutorial zu JTable habe ich zwar ein Beispiel gefunden, in dem
eine JComboBox als cellEditor beschrieben wurde, aber ich
brauche ja keinen Editor sondern eher ein Renderer für Buttons.

Allerdings müssen die Renderer anscheinend von JLabel abgeleitet werden,
was ein Button ja kaum ist. ???:L

Alternativ würde ich mir selbst eine Art Button als JLabel schreiben, aber
wie erfahre ich, ob eine bestimmte Zelle (keine Zeile) selektiert/geklickt wurde?

Danke im Voraus!
 
Hast du vielleicht ein paar Zeilen Code, wie das geht?

Habe gestern mehrere Stunden ergebnislos damit verbracht. 🙁
 
Code:
class ButtonTableCellRenderer extends DefaultTableCellRenderer{
   private JButton button = new JButton("Drück mich");
   public Component getTableCellRendererComponent(JTable table, Object value,
         boolean isSelected, boolean hasFocus, int row, int column) {
         return button;
   }
}
 
Funktioniert nicht so richtig.

Der Button ist zwar jetzt zu sehen, man kann ihn aber nicht anklicken. ???:L
 
ja hättest du das tutorial in der FAQ richtig gelesen, wüsstest du, dass du eben doch einen editor brauchst :roll:
 
Roar hat gesagt.:
ja hättest du das tutorial in der FAQ richtig gelesen

:shock: Huch! Das ist ja richtig fett geworden, seit ich vor zwei Jahren
mal reingeschaut habe.

Ich lad' mir das mal runter und werd' es mir heute abend
bei Wer wird Millionär reinziehen.

Danke!
 
Hier ist eine Lösung, mit der du nicht nur JButton in die JTable tun kannst,
sondern (ohne Anpassung!) auch JComboBox, JList, JRadioButton, usw.
Code:
package table;
/*
 * TableButtons.java
 * Author: Zafir Anjum

Die anonyme Unterklasse von JTable überschreibt die Methoden getCellRenderer() und getCellEditor() 
um jeweils das richtige Objekt zurückzugeben. 
Es wird ausserdem der Default Cell Renderer und Cell Editor für die JComponent Klasse gesetzt. 
"JComponentCellRenderer" implementiert TableCellRenderer und gibt einfach die Komponente der 
darzustellenden Zelle zurück.
"JComponentCellEditor" ist auch ziemlich einfach. Der Code der Event Listener List stammt aus 
einer der Swing Klassen. 
Die Klasse benutzt im Wesentlichen die Komponente der Zelle auch als Editor. 
Nur die Methode shouldSelectCell() ist etwas undurchsichtiger: Sie erzeugt mouse_release 
und mouse_clicked Ereignisse auf der Komponente
um die Komponenten, die solche Ereignisse erwarten, zufriedenzustellen.


 */
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableButtons extends JFrame{
    private JTable table;
    public TableButtons(){
        super("Table Buttons");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(400, 300);
        setLocationRelativeTo(null);
        table = new JTable( 6,4 ) {
            public TableCellRenderer getCellRenderer(int row, int column) {
                TableColumn tableColumn = getColumnModel().getColumn(column);
                TableCellRenderer renderer = tableColumn.getCellRenderer();
                if (renderer == null) {
                    Class c = getColumnClass(column);
                    if( c.equals(Object.class) ) {
                        Object o = getValueAt(row,column);
                        if( o != null )
                            c = getValueAt(row,column).getClass();
                    }
                    renderer = getDefaultRenderer(c);
                }
                return renderer;
            }
            public TableCellEditor getCellEditor(int row, int column) {
                TableColumn tableColumn = getColumnModel().getColumn(column);
                TableCellEditor editor = tableColumn.getCellEditor();
                if (editor == null) {
                    Class c = getColumnClass(column);
                    if( c.equals(Object.class) ) {
                        Object o = getValueAt(row,column);
                        if( o != null )
                            c = getValueAt(row,column).getClass();
                    }
                    editor = getDefaultEditor(c);
                }
                return editor;
            }
        };
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        // Buttons
        JButton btn1 = new JButton("Button 1");
        btn1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                table.getCellEditor().stopCellEditing();
                System.out.println("Button 1");
            }
        });
        JButton btn2 = new JButton("Button 2");
        btn2.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                table.getCellEditor().stopCellEditing();
                System.out.println("Button 2");
            }
        });
        table.setValueAt( btn1, 0, 0 );
        table.setValueAt( btn2, 0, 1 );
        
        table.setDefaultRenderer( JComponent.class, new JComponentCellRenderer() );
        table.setDefaultEditor( JComponent.class, new JComponentCellEditor() );
       
        add( new JScrollPane(table) );
    }
    public static void main(final String[] args) {new TableButtons().setVisible(true);}
}
class JComponentCellRenderer implements TableCellRenderer {
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        return (JComponent)value;
    }
}
class JComponentCellEditor implements TableCellEditor, Serializable {
    protected EventListenerList listenerList = new EventListenerList();
    transient protected ChangeEvent changeEvent = null;
    protected JComponent editorComponent = null;
    public Component getComponent() {
        return editorComponent;
    }
    public Object getCellEditorValue() {
        return editorComponent;
    }
    public boolean isCellEditable(EventObject anEvent) {
        return true;
    }
    public boolean shouldSelectCell(EventObject anEvent) {
        if( editorComponent != null && anEvent instanceof MouseEvent
                && ((MouseEvent)anEvent).getID() == MouseEvent.MOUSE_PRESSED ) {
            Component dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent, 3, 3 );
            MouseEvent e = (MouseEvent)anEvent;
            MouseEvent e2 = new MouseEvent( dispatchComponent, MouseEvent.MOUSE_RELEASED,
                    e.getWhen() + 100000, e.getModifiers(), 3, 3, e.getClickCount(),
                    e.isPopupTrigger() );
            dispatchComponent.dispatchEvent(e2);
            e2 = new MouseEvent( dispatchComponent, MouseEvent.MOUSE_CLICKED,
                    e.getWhen() + 100001, e.getModifiers(), 3, 3, 1,
                    e.isPopupTrigger() );
            dispatchComponent.dispatchEvent(e2);
        }
        return false;
    }
    public boolean stopCellEditing() {
        fireEditingStopped();
        return true;
    }
    public void cancelCellEditing() {
        fireEditingCanceled();
    }
    public void addCellEditorListener(CellEditorListener l) {
        listenerList.add(CellEditorListener.class, l);
    }
    public void removeCellEditorListener(CellEditorListener l) {
        listenerList.remove(CellEditorListener.class, l);
    }
    protected void fireEditingStopped() {
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==CellEditorListener.class) {
                // Lazily create the event:
                if (changeEvent == null)
                    changeEvent = new ChangeEvent(this);
                ((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
            }
        }
    }
    protected void fireEditingCanceled() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==CellEditorListener.class) {
                // Lazily create the event:
                if (changeEvent == null)
                    changeEvent = new ChangeEvent(this);
                ((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
            }
        }
    }
    // implements TableCellEditor
    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int column) {
        editorComponent = (JComponent)value;
         return editorComponent;
    }
}

EDIT:
Wenn du bei shouldSelectCell "true" zurückgibst und im "actionPerformed" vom JButton mit "invokeLater" arbeitest,
dann bekommst du dort mit table.getSelectedRow und table.getSelectedColumn die Koordinaten des geklickten Buttons!
 
Kann ich mit dem oben genannten Code dann auch vollsträndige Spalten mit den Swing Componenten befüllen?
Bei dem gezeigten Beispiel wurden ja nur einzelne Zellen mit setValueAt angepasst.

Mfg
 
Shaguar hat gesagt.:
Kann ich mit dem oben genannten Code dann auch vollsträndige Spalten mit den Swing Componenten befüllen?..
Ja, natürlich. Was für eine Zelle funktioniert, das funktioniert selbstverständlich auch für die anderen.
Setz einfach überall das hin, was du dort jeweils haben willst.
 
Ja aber doch nicht mit setValueAt, oder?
Muss ich da dann für jede Zelle in der Spalte diese Methode aufrufen?
 
Shaguar hat gesagt.:
Ja aber doch nicht mit setValueAt, oder?
Muss ich da dann für jede Zelle in der Spalte diese Methode aufrufen?
Ich verstehe nicht ganz dein Problem. "setValueAt" ist ja nur eine von vielen Möglichkeiten, die JTable
zu aktualisieren. Du kannst das selbstverständlich auch anders machen.
 
Ja ch möchte einfach in einer Spalte nur Comboboxen stehen haben und in einer anderen nur RadioButtons usw.
 
aber vermutlich geht das mit deinem Code nicht sondern muss für jede Spalte einen CellRenderer definieren.
Also über table.getColumn("XYZ").setCellRenderer(new xyzRenderer());
oder irre ich mich etwa?
 
Shaguar hat gesagt.:
aber vermutlich geht das mit deinem Code nicht sondern muss für jede Spalte einen CellRenderer definieren.
Also über table.getColumn("XYZ").setCellRenderer(new xyzRenderer());
oder irre ich mich etwa?
Mit meinem Code müsste es jedenfalls gehen. Versuch's mal :wink:
 
Shaguar hat gesagt.:
und wie müsste ich das implementieren?
Neben den verschiedenen Konstruktoren für JTable und TableModel (Arrays, Vectoren) gibt es dafür
ausser setValueAt z.B. auch noch table.setModel oder model.addRow oder model.insertRow
oder was immer dein TableModel an Aktualisierungsmethoden anbietet :wink:
http://www.java-forum.org/de/viewtopic.php?t=7711

Als Beispiel zeige ich dir hier noch eine Implementation mit Array und "model.insertRow(row, rowData)".
Ich hoffe, das hilft dir weiter:
Code:
package table;
/*
* TableButtons_1.java
* Author: Zafir Anjum / André Uhres
*/
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableButtons_1 extends JFrame {
    private JTable table;
    private String[] columnNames = {"title 1", "title 2", "title 3", "title 4", "title 5", "title 6", "title 7"};
    private DefaultTableModel model;
    private int id;
    public TableButtons_1() {
        super("Table Buttons");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setSize(800, 600);
        setLocationRelativeTo(null);
        model = new DefaultTableModel(createDataArray(), columnNames);
        table = new JTable(model) {
            @Override
            public TableCellRenderer getCellRenderer(final int row, final int column) {
                TableColumn tableColumn = getColumnModel().getColumn(column);
                TableCellRenderer renderer = tableColumn.getCellRenderer();
                if (renderer == null) {
                    Class c = getColumnClass(column);
                    if (c.equals(Object.class)) {
                        Object o = getValueAt(row, column);
                        if (o != null) {
                            c = getValueAt(row, column).getClass();
                        }
                    }
                    renderer = getDefaultRenderer(c);
                }
                return renderer;
            }

            @Override
            public TableCellEditor getCellEditor(final int row, final int column) {
                TableColumn tableColumn = getColumnModel().getColumn(column);
                TableCellEditor editor = tableColumn.getCellEditor();
                if (editor == null) {
                    Class c = getColumnClass(column);
                    if (c.equals(Object.class)) {
                        Object o = getValueAt(row, column);
                        if (o != null) {
                            c = getValueAt(row, column).getClass();
                        }
                    }
                    editor = getDefaultEditor(c);
                }
                return editor;
            }
        };
        table.setDefaultRenderer(JComponent.class, new JComponentCellRenderer_1());
        table.setDefaultEditor(JComponent.class, new JComponentCellEditor_1());
        table.setRowHeight(25);
        add(new JScrollPane(table));
    }

    private Object[][] createDataArray() {
        Object[][] data = new Object[10][];
        for (int row = 0; row < data.length; row++) {
            Object[] rowData = new Object[7];
            // Buttons
            JButton btn1 = getButton();
            JComboBox btn2 = getCombo(row);
            rowData[0] = "" + id++;
            rowData[1] = "";
            rowData[2] = "";
            rowData[3] = "";
            rowData[4] = btn2;
            rowData[5] = "";
            rowData[6] = btn1;
            data[row] = rowData;
        }
        return data;
    }

    private void neueZeile(final int row) {
        Vector<Object> rowData = new Vector<Object>();

        // Buttons
        JButton btn1 = getButton();
        JComboBox btn2 = getCombo(row);
        rowData.add("" + id++);
        rowData.add("");
        rowData.add("");
        rowData.add("");
        rowData.add(btn2);
        rowData.add("");
        rowData.add(btn1);
        model.insertRow(row, rowData);
    }

    private JButton getButton() {
        JButton btn1 = new JButton("Neue Zeile");
        btn1.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                table.getCellEditor().stopCellEditing();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        neueZeile(table.getSelectedRow() + 1);
                    }
                });
            }
        });
        return btn1;
    }

    private JComboBox getCombo(int row) {
        String[] items = {"Item "+row+"_1", "Item "+row+"_2", "Item "+row+"_3"};
        final JComboBox btn2 = new JComboBox(items);
        btn2.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(final ActionEvent e) {
                table.getCellEditor().stopCellEditing();
                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        System.out.println(btn2.getSelectedItem() + "   row: " + table.getSelectedRow());
                    }
                });
            }
        });
        return btn2;
    }

    public static void main(final String[] args) {
        new TableButtons_1().setVisible(true);
    }
}

class JComponentCellRenderer_1 implements TableCellRenderer {
    private JLabel label = new JLabel();
    @Override
    public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
//        if(value instanceof JComboBox){
//            label.setText(((JComboBox)value).getSelectedItem().toString());
//            return label;
//        }
        return (JComponent) value;
    }
}

class JComponentCellEditor_1 implements TableCellEditor, Serializable {

    protected EventListenerList listenerList = new EventListenerList();
    protected transient ChangeEvent changeEvent = null;
    protected JComponent editorComponent = null;

    public Component getComponent() {
        return editorComponent;
    }

    @Override
    public Object getCellEditorValue() {
        return editorComponent;
    }

    @Override
    public boolean isCellEditable(final EventObject anEvent) {
        return true;
    }

    @Override
    public boolean shouldSelectCell(final EventObject anEvent) {
        if (editorComponent != null && anEvent instanceof MouseEvent && ((MouseEvent) anEvent).getID() == MouseEvent.MOUSE_PRESSED) {
            Component dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent, 3, 3);
            MouseEvent e = (MouseEvent) anEvent;
            MouseEvent e2 = new MouseEvent(dispatchComponent, MouseEvent.MOUSE_RELEASED, e.getWhen() + 100000, e.getModifiers(), 3, 3, e.getClickCount(), e.isPopupTrigger());
            dispatchComponent.dispatchEvent(e2);
            e2 = new MouseEvent(dispatchComponent, MouseEvent.MOUSE_CLICKED, e.getWhen() + 100001, e.getModifiers(), 3, 3, 1, e.isPopupTrigger());
            dispatchComponent.dispatchEvent(e2);
        }
        return true;
    }

    @Override
    public boolean stopCellEditing() {
        fireEditingStopped();
        return true;
    }

    @Override
    public void cancelCellEditing() {
        fireEditingCanceled();
    }

    @Override
    public void addCellEditorListener(final CellEditorListener l) {
        listenerList.add(CellEditorListener.class, l);
    }

    @Override
    public void removeCellEditorListener(final CellEditorListener l) {
        listenerList.remove(CellEditorListener.class, l);
    }

    protected void fireEditingStopped() {
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (changeEvent == null) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i+1]).editingStopped(changeEvent);
            }
        }
    }

    protected void fireEditingCanceled() {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == CellEditorListener.class) {
                // Lazily create the event:
                if (changeEvent == null) {
                    changeEvent = new ChangeEvent(this);
                }
                ((CellEditorListener) listeners[i+1]).editingCanceled(changeEvent);
            }
        }
    }

    // implements TableCellEditor
    @Override
    public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected, final int row, final int column) {
        editorComponent = (JComponent) value;
        return editorComponent;
    }
}
 
Vielen Dank, hat mir sehr weitergeholfen. Aber ein Frage noch :
Wie ist das von der Performance her wenn ich auf diese Art und Weise eine Tabelle mit ca 1000 Einträgen befülle?
 
achja doch noch was.
Was macht eigentlich die Funktion shouldSelectCell()
Wenn ich den Code in der Funktion lösche und nur return true stehen lasse, funktioniert auch weiterhin alles 1a
 
"shouldSelectCell" hab ich von Zafir übernommen. Er schreibt dazu:
The only mysterious code is in shouldSelectCell().
What it does is create a dummy mouse_release and mouse_clicked events on the component.
The reason for this is that the TableUI class passes on the event that initiated the edit ( the mouse_pressed event ) to the editor component.
Since many components expect mouse_released and mouse_clicked to follow, we oblige these components.

In meinem Beispiel ist tatsächlich auch eine Auswirkung sichtbar, wenn man den Code wegnimmt:
Wenn man dann den Button "Neue Zeile" nacheinander bei verschiedenen Zeilen anklickt,
dann wird nur jedes zweite Mal auch eine neue Zeile angelegt :wink:
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben