jTable mit jButton - nach refresh 2 mal Action

Myar

Mitglied
Hallöchen!

Folgende Situation:
Ich habe eine jTable deren AbstractModel zur Laufzeit Daten aus der DB erhält.

Ich übergebe der Tabelle zudem noch nen CellRenderer etc:

Aufbereiten der Tabelle:

Code:
private void setTableRendererGames() {
        MyTableModelGames gamesmodel = new MyTableModelGames();
        jTable2.setModel(gamesmodel);
        jTable2.getColumnModel().getColumn(0).setPreferredWidth(55);
        jTable2.getColumnModel().getColumn(1).setPreferredWidth(260);
        jTable2.getColumnModel().getColumn(2).setPreferredWidth(200);
        jTable2.getColumnModel().getColumn(3).setPreferredWidth(270);
        jTable2.getColumnModel().getColumn(4).setPreferredWidth(55);
        jTable2.getColumnModel().getColumn(5).setPreferredWidth(70);
        jTable2.getColumnModel().getColumn(6).setPreferredWidth(100);
        jTable2.getColumnModel().getColumn(7).setPreferredWidth(100);
        jTable2.getColumnModel().getColumn(8).setPreferredWidth(53);
        jTable2.getColumnModel().getColumn(9).setPreferredWidth(102);
        jTable2.setDefaultRenderer(Integer.class, new MyRendererInteger());
        jTable2.setDefaultRenderer(String.class, new MyRenderer());
        TableCellRenderer buttonRenderer = new JTableButtonRenderer();
        jTable2.getColumn("Links anzeigen").setCellRenderer(buttonRenderer);
        jTable2.addMouseListener(new JTableButtonMouseListener(jTable2));
        jTable2.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        jTable2.setRowHeight(40);
        jTable2.getColumnModel().getColumn(3).setCellEditor(new JTextAreaEditor());
        jTable2.getColumnModel().getColumn(3).setCellRenderer(new JTextAreaRenderer());
        setUpSportColumn(jTable2, jTable2.getColumnModel().getColumn(6),jTable2.getColumnModel().getColumn(7));
        setTablesorter(gamesmodel);
    }

Das Model holt sich Daten aus der Datenbank und setzt in der entsprechenden Spalte einen Button mit einem ActionListener:

getValueAt() Methode aus AbstractTableModel wird überschrieben und am Ende steht halt:

Code:
else{
                    final JButton button2 = new JButton(this.columnNames[col]);
                    button2.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent arg0) {
                            new GameLinks(id.get(row),name.get(row),false).setVisible(true);
                        }
                    });
                    return button2;
                }

So, der Button funktioniert auch wunderbar. Nur wenn ich an anderer Stelle eine Änderung an der Datenbank vornehme (lösche eine Zeile oder füge eine hinzu), dann möchte ich gerne, dass sich die jTable2 auch aktualisiert.
Dazu habe ich erstmal einen Button, der folgendes tut:

Code:
private void jButton22ActionPerformed(java.awt.event.ActionEvent evt) {
        setTableRendererGames();        
}

Danach führt der Button seine Action zwei mal aus, wenn man darauf klickt. Wo vorher nur ein Window geöffnet wurde, werden nun zwei geöffnet. Drücke ich nochmal refresh, wird die Action drei mal ausgeführt. Das steigert sich linear.

Ich verstehe das einfach nicht und würde das gerne beheben.
Kann mir da wer helfen?

Hier noch der restliche Code, der für den Button und die Dropdowns in der Tabelle ist:

Code:
public void setUpSportColumn(JTable table,
                                 TableColumn system,TableColumn genre) {
        //Set up the editor for the sport cells.
        comboBoxSystem = new JComboBox(new JComboBoxModelSystem());
        comboBoxGenre = new JComboBox(new JComboBoxModelGenre());
        system.setCellEditor(new DefaultCellEditor(comboBoxSystem));
        genre.setCellEditor(new DefaultCellEditor(comboBoxGenre));
    }
    private void setTablesorter(MyTableModelGames gamesmodel){
        final TableRowSorter<MyTableModelGames> sorter = new TableRowSorter<>(gamesmodel);
        jTable2.setRowSorter(sorter);
        jTable2.setFillsViewportHeight(true);
        filterText.getDocument().addDocumentListener(
                new DocumentListener() {
                    @Override
                    public void changedUpdate(DocumentEvent e) {
                        newFilter(sorter);
                    }
                    @Override
                    public void insertUpdate(DocumentEvent e) {
                        newFilter(sorter);
                    }
                    @Override
                    public void removeUpdate(DocumentEvent e) {
                        newFilter(sorter);
                    }
                });
    }
    private void newFilter(TableRowSorter<MyTableModelGames> sorter) {
        RowFilter<MyTableModelGames, Object> rf;
        //If current expression doesn't parse, don't update.
        try {
            rf = RowFilter.regexFilter("(?i)" + filterText.getText(), 1);
        } catch (java.util.regex.PatternSyntaxException e) {
            return;
        }
        sorter.setRowFilter(rf);
    }
   public static class JTableButtonRenderer implements TableCellRenderer {
       @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
           JButton button = (JButton)value;
           if (isSelected) {
               button.setForeground(table.getSelectionForeground());
               button.setBackground(table.getSelectionBackground());
           } else {
               button.setForeground(table.getForeground());
               button.setBackground(UIManager.getColor("Button.background"));
           }
           return button;
       }
   }
   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(); 
           if (row < table.getRowCount() && row >= 0 && column < table.getColumnCount() && column >= 0) {
               Object value = table.getValueAt(row, column);
               if (value instanceof JButton) {
                   ((JButton)value).doClick();
               }
           }
       }
   }

Viele Grüße
Myar
 

Improof

Erfahrenes Mitglied
Hi,

kleine Frage erstmal: Sicher, dass der Anstieg der Aufrufe "1-2-3-4..." ist und nicht "1-2-4-8..."?
Im zweiten Fall wüsste ich die Ursache, und zwar ist so ein exponentieller Anstieg dann möglich, wenn in der ActionPerformed-Methode (oder irgendeiner anderen Methode, die von dieser heraus aufgerufen wird) der ActionListener an den Button gehängt wird. In deinem Code hab ich so ein Konstrukt jetzt aber auch nicht gesehen.

Allerdings ist mir aufgefallen, dass du bei jedem Aufruf der setTableRendererGames-Methode zwar das Model neu erstellst, aber nicht die Tabelle selbst. So addest du jedes mal einen zusätzlichen MouseListener und in diesem führst du ein doClick() auf den Buttons aus...könnte zu ähnlichem Verhalten führen, wie der obere Ansatz.

Trotzdem ist es mir ein Rätsel, wie du auf einen linearen Anstieg der Aufrufe kommst.

Mal zur Verdeutlichung:
1. Click -> 1 mal doClick() -> 1 neuer MouseListener
2. Click -> 2 mal doClick() -> 2 neue MouseListener
3. Click -> 4 mal doClick() -> 4 neue MouseListener
usw.

So verhält es sich zumindest, wenn du den ActionListener (wie erwähnt) aus der ActionPerformed-Methode hinzufügst.

Ich hoffe, ich konnte ein bisschen helfen ;)


Gruß
Daniel
 

Myar

Mitglied
Hi Improof,

danke erstmal für deine Antwort.

Ich bin mir jetzt nicht mehr 100% sicher, dass der Anstieg linear ist ;) Ich werde das nochmal ausführlicher testen und dann berichten. Die jTable neu zu erstellen werde ich auch direkt testen.

Aber ja, deine Antwort hat mich durchaus etwas schlauer gemacht ;)

Gruß
Myar
 

Improof

Erfahrenes Mitglied
Hi Myar,

bitte, ich helfe immer gerne ;)

Und ja, teste das mal aufmerksam, ich bin jetzt schon auf das Ergebnis gespannt

Gruß
Daniel
 

Myar

Mitglied
Hi,

also tatsächlich war nach einem Refresh der Action Aufruf Linear. Also 1-2-3-4-5...

Habs 5 mal getestet.

Aber dieser Abschnitt mit in die Methode hat den Fehler behoben:

Code:
        MyTableModelGames gamesmodel = new MyTableModelGames();
        jTable2 = new JTable(gamesmodel);
        jTable2.setAutoCreateRowSorter(true);
        jTable2.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
        jScrollPane4.setViewportView(jTable2);

Ob das nun das Rätsel löst, warum der Anstieg linear und nicht exponenziell ist, musst Du mir sagen ;)

Gruß
Myar
 

benhaze

Mitglied Platinum
siehe deine Methode setTableRendererGames()

bei jedem Aufruf wird immer der gleichen jTable2 1xMouseListener hinzugefügt.
Java:
setTableRendererGames() {
        MyTableModelGames gamesmodel = new MyTableModelGames();
        jTable2.setModel(gamesmodel); //Table-Objekt scheint bereits zu existieren
        //...
        jTable2.addMouseListener(new JTableButtonMouseListener(jTable2)); //hier wird der gleichen Table immer ein neuer Listener hinzugefügt
        //..
Deswegen linear bei *refresh*.

Dein workaround funktioniert, weil du jetzt folgendes machst:
Java:
 jTable2 = new JTable(gamesmodel);

du erstellt ein neues Table-Objekt und fügst diesem neuen Objekt einen Listener hinzu.
 
Zuletzt bearbeitet:

Improof

Erfahrenes Mitglied
Hi,

@Myar:
Dann konnte ich ja wenigstens mit der Aussage, dass du die JTable nicht neu erstellst, helfen ;)


@benhaze:
Aber müsste dann nicht theoretisch beim zweiten mal auch beide MouseListener aufgerufen werden? Wenn man das gleiche mit ActionListenern auf Buttons macht (wie ich schon in einem vorherigen Post erwähnt habe), dann geschieht nämlich genau das: 1 Aufruf -> 2 Aufrufe -> 4 Aufrufe usw.

Kannst mir das mal genauer erklären? Macht Java hier bei MouseListenern etwas anders?


Gruß
Daniel


EDIT:
Also ich hab das jetzt mal selbst getestet. Nicht mit dem Code von Myar, ich hab "mal schnell" das ganze in etwa nachgestellt.
2 Komponenten, die eine hat einen MouseListener, die andere einen ActionListener.
(Weil mir eine Tabelle jetzt zu viel Aufwand mit Renderern gewesen wäre, habe ich testweise zwei Buttons genommen)
Der Button mit dem MouseListener führt ein doClick() auf den anderen Button aus. Dieser wiederum fügt in seinem ActionListener jeweils einen weiteren MouseListener zu dem anderen Button hinzu. Im ActionListener habe ich die Ausgabe "CLICKED".

Selbes Ergebnis wie ich vermutet habe: exponenzieller Anstieg der Aufrufe.
Wie aber kommt das dann hier zustande? Hat es etwas mit der JTable zu tun?
 
Zuletzt bearbeitet:

benhaze

Mitglied Platinum
1. Der MouseListener reagiert nur auf Mouse-Events.
2. Der Action-Listener reagiert auf alles was als Action definiert ist. (z.B Mouse-Click und Enter-Taste, evtl auch die Leertaste)
3. die JTable hat nichts damit zu tun

Bei dir ist der Anstieg exponentiell, weil deine Code-Logik eine andere ist.
Dein Button-A ruft Button-B auf.
Button-B fügt einen Listener zu Button-A.
(Es sind immer die gleichen Buttons im Spiel!)
Wenn du erneut Button-A klickst werden nun alle registrierten Event-Handler aufgerufen.
Jeder dieser Handler registriert einen neuen Listener (beim gleichen Button-A-Objekt!).

Er hat nicht das Problem mit dem exp. Anstieg, da er ständig einen neuen Button zaubert, siehe:

Java:
final JButton button2 = new JButton(this.columnNames[col]);
                    button2.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent arg0) {
                            new GameLinks(id.get(row),name.get(row),false).setVisible(true);
                        }
                    });
                    return button2;

Selbst wenn sein Listener 100 weitere Listener diesem Button hinzufügt, so ist es egal.
Da beim nächsten Aufruf sowieso erst wieder ein neuer Button erzeugt wird.

Sein neuer Button hat initial keine Listener.
Dein Button hingegen ist immer der gleiche (und hat ja auch bereits selber registrierte Listener vom letzten Klick)!

Also sein Code und dein Code sind in der Logik schon ziemlich unterschiedlich.

Ich hoffe ich habe mich in etwa verständlich ausgedrückt.
 
Zuletzt bearbeitet:

Improof

Erfahrenes Mitglied
Hi benhaze,

ja danke, das war genau der Denkfehler, den ich hatte. Dass er immer einen neuen Button erzeugt, muss ich wohl irgendwie übersehen haben. so ist es jetzt klar, warum der Anstieg linear und nicht exponentiell ist.

Aber wie gesagt vielen Dank für die umfassende Erklärung :)


Gruß
Daniel