2016-01-19 8 views
5

Ich habe nie mit Timer s vor gearbeitet, so ist mein Problem wahrscheinlich wirklich dumm. Mein Programm zeichnet einen Kreis, der rot ist und nach zufälligen Sekunden sollte der Kreis seine Farbe auf grün ändern. Ich habe gerade einen Swing-Timer erstellt, wie Sie unten im Code sehen können. Und es tritt actionPerformed() Methode, aber es ändert sich nicht die Farbe. Könnten Sie mir helfen, mein Problem mit den Farben zu beheben?Painting in Swing Timer funktioniert nicht

Mein Code:

package igrica; 
import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Random; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.Timer; 


public class ChangingCircle implements ActionListener{ 

JFrame frame; 

Timer timer; 
Random r; 

public static void main(String[] args) { 
    ChangingCircle gui = new ChangingCircle(); 
    gui.go(); 
} 

public void go() { 
    frame = new JFrame(); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

    MyPanel panel = new MyPanel(); 

    frame.getContentPane().add(BorderLayout.CENTER, panel); 
    frame.setSize(300, 300); 
    frame.setVisible(true); 
} 

public void actionPerformed(ActionEvent event) { 
    frame.repaint(); 
} 

class MyPanel extends JPanel { 
    public void paintComponent(Graphics g) { 


     g.setColor(Color.red); 
     g.fillOval(100, 100, 100, 100); 

     Random r = new Random(); 

     Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() { 
      public void actionPerformed(ActionEvent ev) { 
       System.out.println("Timer out"); 
       g.setColor(Color.green); 
       g.fillOval(100, 100, 100, 100); 
      } 
     }); 
     timer.start(); 
    } 
} 
} 
+0

Tipp: Beginnen Sie nicht jeden Satz mit "So", Sie erzählen keine Geschichte. – user1803551

+0

Ich weiß, ich sollte nicht, aber ich weiß nicht mit welchem ​​Wort einen Satz beginnen. xD –

+0

Entfernen Sie einfach "So" und der Satz ist in Ordnung, wie es ist. – user1803551

Antwort

8

Es gibt ziemlich viel Chaos in Ihrem Code. Versuchen Sie folgendes:

public class ChangingCircle { 

    Color color = Color.RED; 
    MyPanel panel = new MyPanel(); 

    public static void main(String[] args) { 

     SwingUtilities.invokeLater(() -> { 
      ChangingCircle gui = new ChangingCircle(); 
      gui.go(); 
     }); 
    } 

    public void go() { 

     JFrame frame = new JFrame(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     frame.getContentPane().add(panel, BorderLayout.CENTER); 
     frame.pack(); 
     frame.setVisible(true); 

     Random r = new Random(); 
     Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() { 

      public void actionPerformed(ActionEvent ev) { 

       System.out.println("Timer"); 
       color = Color.GREEN; 
       panel.repaint(); 
      } 
     }); 
     timer.setRepeats(false); 
     timer.start(); 
    } 

    class MyPanel extends JPanel { 

     private int size = 100, loc = 100; 

     @Override 
     public void paintComponent(Graphics g) { 

      super.paintComponent(g); 
      g.setColor(color); 
      g.fillOval(loc, loc, size, size); 
     } 

     @Override 
     public Dimension getPreferredSize() { 

      return new Dimension(size + loc, size + loc); 
     } 
    } 
} 

Die Idee ist, dass der Timer nur die Eigenschaft der Form verändert werden gezogen und ruft dann repaint() die Änderung widerzuspiegeln. Die paintComponent wird immer dann aufgerufen, wenn sie benötigt wird, auch in schneller Folge und sollte schnell zurückgehen.

Besondere Hinweise:

  • Start Swing from the EDT.
  • Erstellen und starten Sie den Timer von außerhalb von paintComponent, da es viele Male aufgerufen wird und das wird viele Timer erstellen und starten.
  • Sie sollten wahrscheinlich den Timer nicht wiederholen.
  • Rufen Sie als das erste Ding innerhalb paintComponent.
  • Sie scheinen eine ActionListener zu haben, die nichts tut.

Allgemeine Tipps:

  • Verwenden Sie die @Override Anmerkung anwendbar, wenn.
  • Rufen Sie pack() auf dem Rahmen, anstatt seine Größe manuell einzustellen und @Override die getPreferredSize Methode der Komponente, die Sie malen.Geben Sie eine sinnvolle Größe basierend auf dem, was Sie zeichnen, zurück.
  • Verwenden Sie add(component, location) und nicht umgekehrt (veraltet).
  • Verwenden Sie keine Felder, wenn lokale Variablen funktionieren (z. B. Random r).
  • Verwenden Sie konstante Großbuchstaben (Color.RED anstelle von Color.red).
+0

Danke, du hast es so genau beschrieben! –

6

Sie initiieren keinen Timer aus einem paintcomponent-Methode. Diese Methode sollte nur zum Malen und Malen verwendet werden. Stattdessen starten Sie den Timer in Ihrem Konstruktor und innerhalb des Timers actionPerromed und rufen Sie repaint(), ändern Sie den Status eines Feldes der Klasse, und verwenden Sie diese Informationen in der PaintComponent verwenden Sie dieses Feld, um neue Informationen zu zeichnen.

z.B.

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Random; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.Timer; 

public class ChangingCircle { 
    JFrame frame; 

    public static void main(String[] args) { 
     ChangingCircle gui = new ChangingCircle(); 
     gui.go(); 
    } 

    public void go() { 
     frame = new JFrame(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     MyPanel panel = new MyPanel(); 

     frame.getContentPane().add(BorderLayout.CENTER, panel); 
     frame.setSize(300, 300); 
     frame.setVisible(true); 
    } 

    public void actionPerformed(ActionEvent event) { 
     frame.repaint(); 
    } 

    class MyPanel extends JPanel { 
     private Random r = new Random(); 
     private boolean draw = false; 

     public MyPanel() { 
      Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() { 
       public void actionPerformed(ActionEvent ev) { 
        draw = true; 
        repaint(); 
       } 
      }); 
      timer.setRepeats(false); 
      timer.start(); 
     } 
     public void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      if (draw) { 
       g.setColor(Color.red); 
       g.fillOval(100, 100, 100, 100); 
      } 
     } 
    } 
} 

Vergessen Sie auch nicht, die paintComponent-Methode des Supervisors innerhalb Ihrer Überschreibung aufzurufen.

Wenn Sie Farben ändern müssen, geben Sie der JPanel ein Farbfeld, sagen wir color und ändern Sie den Wert aus dem Timer, und rufen Sie dann repaint(). Verwenden Sie in paintComponent erneut den Wert dieses Felds, um das Oval mit zu zeichnen. Auch in dieser Situation sollte der Timer wiederholen, also timer.setRepeats(false) in dieser Situation loswerden.

+0

aber was zu tun ist, wenn ich ständig die Farbe von ex ändern muss. rot zu grün und dann grün zu rot, wird das auch funktionieren? –

+0

@DomagojSabolic: Ändern Sie dann im Timer den Zustand eines Farbfelds und verwenden Sie dieses in der paintComponent-Methode. –

+0

@DomagojSabolic: siehe zu beantwortende Änderungen. –

0

Der Timer arbeitet asynchron und paintComponent endet vor dem Abschluss der Arbeit des Timers.

+0

Aber mein System.out.println funktioniert und druckt jedes Mal, wenn der Timer –

+0

in diesem Beispiel beendet ist, haben Sie einen JFrame und einen Jpanel, wenn Sie viele Komponenten hinzufügen, wird diese Implementierung Ausnahmen verursachen, wenn Ihr Timer versucht, Änderungen an g vorzunehmen , in der gleichen Zeit wird eine andere Komponente die gleiche Instanz von g verwenden, um neu zu streichen –

+0

Ich sehe nicht, wie dies die Frage beantwortet. 'paintComponent' * sollte/kann * fertig sein, bevor der Timer startet oder seine Aktion ausführt. Das Problem der Asynchronität ist irrelevant. – user1803551