JButton Text 2 Mal ändern funktioniert nicht

Carcharoth

Bekanntes Mitglied
Guten Tag

Ich will exemplarisch den Text eines JButtons 2 Mal nacheinander ändern (mit einer kleinen Pause dazwischen). Solange ich dies ausserhalb des ActionListeners mache, funktioniert es perfekt. Wenn ich den Code in den ActionListener / actionPerformed einbaue, zeigt er mir die erste Textänderung nicht mehr an (es sieht so aus, als würde er nur die Pause und die zweite Textänderung machen).

Ich habe das Gefühl es hängt entweder mit dem Repaint-Aufruf oder mit der Sichtbarkeit des Buttons innerhalb von actionPerformed zusammen. Aber ich komme nicht darauf.

Danke für eure Hilfe.
Gruss

Java:
public class Fenster extends JFrame {
    
    private JPanel contentPane;
    
    public Fenster() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        setContentPane(contentPane);
        
        JButton meinButton = new JButton("New button");
        contentPane.add(meinButton);
        
        setVisible(true);
        
        meinButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (e.getSource()==meinButton) {
                    meinButton.setText("aaaa");
                    meinButton.repaint();
                    
                    try {
                        TimeUnit.MILLISECONDS.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    
                    meinButton.setText("bbbb");
                    meinButton.repaint();
                }
            }
        });
    }
}
 
Du blockierst mit dem ActionListener den Event Dispatch Thread. Veränderungen der UI müssen im Event Dispatch Thread stattfinden, das "Warten" muss in einem eigenen Thread stattfinden. Du musst innerhalb des ActionListener innerhalb eines Threads "warten" und innerhalb des Event Dispatch Thread die UI ändern:
Java:
import java.awt.event.*;
import java.util.concurrent.TimeUnit;

import javax.swing.*;

public class Fenster extends JFrame {
    private JPanel contentPane;

    public Fenster() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        setContentPane(contentPane);

        JButton meinButton = new JButton("New button");
        contentPane.add(meinButton);

        setVisible(true);

        meinButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (e.getSource() == meinButton) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    meinButton.setText("aaaa");
                                    meinButton.repaint();
                                    meinButton.setEnabled(false);
                                }
                            });
                            try {
                                TimeUnit.MILLISECONDS.sleep(2500);
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                            SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    meinButton.setText("bbbb");
                                    meinButton.repaint();
                                    meinButton.setEnabled(true);
                                }
                            });
                        }
                    }).start();
                }
            }
        });
    }

    public static void main(String[] args) {
        new Fenster();
    }
}
 
Hallo BestGoalKeeper

Vielen Dank für deine Hilfe. Es funktioniert einwandfrei.

Die Syntax ist für mich noch etwas schwer nachzuvollziehen aber ich will zuerst sicherstellen, dass ich den Gesamtmechanismus verstanden habe.

Verstehe ich das also richtig, dass das Warten (Sleep) im Thread des ActionListeners (hier new Thread) läuft während die beiden invokelater-Blocks erlauben das Update der UI im Event Dispatch Thread auszuführen?

Gruss
 
Verstehe ich das also richtig, dass das Warten (Sleep) im Thread des ActionListeners (hier new Thread) läuft während die beiden invokelater-Blocks erlauben das Update der UI im Event Dispatch Thread auszuführen?
Ja. Der EDT arbeitet eine Warteschlange (Event Queue) ab, in der zu verarbeitende Ereignisse oder auch Runnables liegen. Mit invokeLater wird ein Runnable in diese Warteschlange eingereiht.
 
Mit einer Grafik ist das einfacher zu erklären, Du blockierst mit dem ActionListener den EDT, der auch für die Aktualisierung der UI zuständig ist:

bi1.png
 
Ja die Graphik hilft wirklich um es zu verstehen. Danke.

Für meinen konkreten Fall habe ich die Struktur mit den Invokelater in eine For Schleife eingebaut.
Wie kann ich den Zähler t in die Methode run() innerhalb des invokelaters übergeben?

[CODE lang="java" highlight="22;26"]public class Fenster extends JFrame {

private JPanel contentPane;

public Fenster() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
setContentPane(contentPane);

JButton meinButton = new JButton("New button");
contentPane.add(meinButton);

setVisible(true);

meinButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == meinButton) {
new Thread(new Runnable() {
@Override
public void run() {
for (int t=5;t>=0;t--) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
meinButton.setText("t=" + t);
meinButton.repaint();
}
});

try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
}).start();
}
}
});
}
}[/CODE]
 
Wie kann ich den Zähler t in die Methode run() innerhalb des invokelaters übergeben?
Indem Du eine lokale Variable final deklarierst:
Java:
                           for (int t=5;t>=0;t--) {
                                final int value = t;
                                SwingUtilities.invokeLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        meinButton.setText("t=" + value);
                                        meinButton.repaint();
                                    }
                                });
 

Zurück
Oben