ActionListener bei Buttons

Java xyrse123

Bekanntes Mitglied
Hallo,
ich wollte eine kleine grafische Oberfläche machen wo ein Countdown abläuft, das ganze funktioniert auch. Nur wollte ich noch 2 Buttons einbauen mit denen man das ganze starten und stoppen kann, doch irgendwas funktioniert mit dem ActionListener und den Buttonereignissen nicht. Hier mein Code:

Code:
public class Countdown extends JFrame implements Runnable {
    int c = 10;
    private JButton b1;
    private JButton b2;

    public Countdown() {
        setSize(300, 300);
        setTitle("Countdown");
        add(new MyPanel());
        setVisible(true);

        // startAnimation();
    }

    class MyPanel extends JPanel implements ActionListener {

        MyPanel() {
            setBackground(Color.lightGray);
            setLayout(null);

            JButton b1 = new JButton("Start");
            b1.setBounds(50, 0, 75, 20);
            b1.setBackground(Color.GREEN);
            b1.addActionListener(this);
            b1.setFocusable(true);
            add(b1);
           
            JButton b2 = new JButton("Stop");
            b2.setBounds(150, 0, 75, 20);
            b2.setBackground(Color.RED);
            add(b2);

        }

        public void paint(Graphics g) {
            super.paint(g);
            g.setColor(Color.BLACK);
            g.setFont(new Font("Arial", Font.BOLD, 50));

            g.drawString("" + c, 120, 150);
            --c;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == b1) {
                System.out.println("Test");
                startAnimation();
            }          
        }
    }

    public void run() {
        while (c >= 0) {
            repaint();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {

            }

        }

    }

    public void startAnimation() {
        Thread t = new Thread(this);
        t.start();
    }
    public static void main(String[] args) {
        new Countdown();
    }

}
 
MyPanel() {
....
JButton b1 = new JButton("Start");
....
}
und
private JButton b1;
...
if (e.getSource() == b1)
sind 2 komplett verschiedene Objekte.

Im Konstruktor erstellst du noch mal eine lokale b1 Variable, welche nichts mit deinem globalen Objekt zu tun hat.
Im ActionListener prüfst du aber ob der gedrückte Button gleich deinem globalen Button ist - was nicht zutrifft.
Anstatt im Konstruktor eine lokale Variable zu erstellen solltest du deine globale Variable initialisieren.
 
Danke jetzt funktioniert es.
Doch ein Problem gibt es noch.
Wie kann ich den Thread unterbrechen, wenn ich auf "stop" drücke und das er dann erst wieder startet wenn ich auf "start" drücke.
 
So wie ich dein Vorhaben verstehe kannst du es leider nicht umsetzen.
Threads können nicht gestoppt und dann wieder neu gestartet werden.
Was du machen musst ist jedes mal einen neuen Thread zu erstellen und den zu starten. (Hierfür lagerst du deine Logik am Besten in ein Runnable Objekt aus welches du den neuen Threads übergeben kannst).
 
Vergiss was ich gesagt habe - hatte mir dein Code nicht noch mal angeschaut.
---
An sich passt dein Konstrukt ja schon.
Jedes mal wenn der User auf start drückt (und aktuell der Counter nicht läuft) soll ein neuer Thread gestartet werden.
Wenn der User auf stoppt drückt soll der aktuelle Thread gestoppt werden. Dazu müsstest du den Thread den du startest noch als globale Variable halten damit du ihn von überall stoppen kannst.
 
Gibt es noch eine andere Möglichkeit den Thread zu unterbrechen, den so bekomme ich eine Nullpointer Eception.
Code:
if (e.getSource() == b2) {    
    t.interrupt();        
   }
 
Die NPE bekommst du sicherlich weil du ca so ein Konstrukt hast:
Java:
private Thread t;

@Override
public void actionPerformed(ActionEvent e) {
    if(e.getSource() == b2) {
         t.interrupt();
    }
}

public void startAnimation() {
     Thread t = new Thread(this);
     t.start();
}
Oder? Wenn ja dann ist es der gleiche Fehler wie bei deiner Ausgangsfrage 😉
 
Ich wollte nur noch fragen ob ich das ganze auch noch anders lösen kann als mit dem flag.
Hier der Code:
Code:
  @Override
  public void actionPerformed(ActionEvent e) {
   Graphics g=getGraphics();
   if (e.getSource() == b1) {
    System.out.println("Test");
    startAnimation();
       flag=true;
   }
   if (e.getSource() == b2) {
    System.out.println("Test");
    t.interrupt();
    flag=false;    
    
   }
   if(e.getSource()==b3) {  // Reset Button
    t.interrupt();
    flag=false;
    c=10;
    g.setFont(new Font("Arial", Font.BOLD, 50));
    g.drawString("" + c, 120, 150);
   repaint();
   }
   
  }
 }
 public void run() {
  while(c>=0 && flag==true) { 
   repaint();
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
   }
  }
 }
 
Wenn ich das Fenster in Hintergrund schiebe und dann wieder õffne oder wenn ich es vergrössere dann wird der Zähler einen runter gesetzt. Kann mir wer erklären woran das liegt?
 
Wenn ich das Fenster in Hintergrund schiebe und dann wieder õffne oder wenn ich es vergrössere dann wird der Zähler einen runter gesetzt. Kann mir wer erklären woran das liegt?
Wenn du das Fenster in den Fordergrund holst, muss es neu gezeichnet werden. Dazu wird paint aufgerufen, und darin wird der Zähler hochgesetzt.
Ein paar grundsätzliche Sachen:
1. In der paint (besser: paintComponent) Methode sollte nur gezeichnet werden!
2. Es sollte nur in der paintComponent-Methode gezeichnet werden! In deiner actionPerformed holst du dir einen Graphics und zeichnest da drauf. Das mag ja in den meisten Fällen so aussehen, als würde es funktionieren, aber irgendwann fällst du damit auf die Nase.
3. Bei Animationen mit Swing würde ich dringend dazu raten, das ganze mit einer Instanz von java.util.Timer zu machen. Hier ein Beispiel:
Java:
package swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;


public class AnimatedTextPanel extends JComponent{

    private String text;
   
    private int cursor;
   
    public static void main(String[] args){
        JFrame frame = new JFrame("Animated Text");
        AnimatedTextPanel panel = new AnimatedTextPanel("Hier_ist_der_Text");
        Timer timer = new Timer(50, new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                panel.updateText();
            }
        });
        JButton switcher = new JButton("Start");
        switcher.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent ae){
                if(timer.isRunning()){
                    timer.stop();
                    switcher.setText("Start");
                }
                else{
                    timer.start();
                    switcher.setText("Stop");
                }
            }
        });
        frame.add(panel);
        JPanel buttons = new JPanel();
        buttons.add(switcher);
        frame.add(buttons, BorderLayout.NORTH);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
   
    public AnimatedTextPanel(String text){
        this.text = text;
        cursor = 0;
    }
    public void updateText(){
        cursor++;
        if(cursor > text.length()) cursor = 0;
        repaint();
    }
    public void paintComponent(Graphics g){
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        Dimension dim = super.getSize();
        g2.setColor(Color.RED);
        g2.setFont(new Font("Arial",1, 24));
        String toShow = text.substring(0, cursor);
        FontMetrics fm = g2.getFontMetrics();
        Rectangle2D rect = fm.getStringBounds(toShow, g);
        g2.drawString(toShow, dim.width/2 - (int)(rect.getWidth()/2), dim.height/2 +  (int)(rect.getHeight()/2));
    }
   
}
 
Eine bessere API: Timer können ganz einfach gestartet und gestoppt werden. Ausserdem kann der delay einfach geändert werden.
Eine aus meiner Sicht passendere Herangehensweise: Threads sollen einen Job erledigen, und nicht die meiste Zeit einfach nur warten, um dann jemand anderem zu sagen, etwas zu tun.
Threadersparnis: AFAIK teilen sich mehrere Timer einen gemeinsame Thread, in dem sie ihren Countdown runterzählen und auf ihren nächsten Einsatz warten.
Schliesslich: javax.swing.Timer kommen nie mit dem EDT ins Gehege, und werden ihn nie blockieren. Thread.sleep() legt den aktuellen Thread schlafen, und wenn das der EDT ist, friert die gesamte GUI ein.
In deinem Fall sieht deine Herangehensweise ok aus, aber ich würde empfehlen, es von Anfang an richtig zu machen.
 
Java:
import javax.swing.Timer;
javax.swing.Timer

welchen von beiden denn jetzt? ;P

javax.swing.Timer kommen nie mit dem EDT ins Gehege, und werden ihn nie blockieren.
Wobei man da bedenken muss, dass die ausgelöste Aktion schon im EDT-Thread läuft, mit allen Vor und Nachteilen, und diesen deshalb natürlich auch blockieren kann.
 

Neue Themen


Zurück
Oben