Animation von Kreisen

Mane123

Bekanntes Mitglied
Hallo zusammen,

ich habe gerade folgende Klasse (als Anfänger und ohne fremder Hilfe *stolz bin*) erstellt:

Java:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.JPanel;
import javax.swing.Timer;

//Die Klasse Animation Kreis stellt den größer werdenen Kreis dar
public class AnimationKreis extends JPanel {
	
	//eine Instanzvariable für den Timer
	private Timer timer;
	//eine Instanzvariable für das Bild welches den Hintergrund darstellt
	private BufferedImage bild;
	//für die Breite und die Höhe des Kreises 
	int breite = 100, hoehe = 100;
	
	//eine innere Klasse für den Timer
	
	class TimerListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO Auto-generated method stub
			//Koordinaten für die obere linke Ecke
			int x, y;	
			
			//den Grafikkontext des Bildes beschaffen und überschreiben
			Graphics g = bild.getGraphics();
			g.fillRect(0,0,getWidth(),getHeight());
			
			//die Mitte der Kreise berechnen
			//Von der Mitte des Panels wird jeweils die
			//Hälfte der Breite und der Höhe subtahiert.
			//Dadurch werden alle Kreise zentriert dargestellt
			x = getWidth()/2-breite/2;
			y = getHeight()/2-hoehe/2;
		
			//die Kreise zeichnen
			paintOvals(x,y,breite,hoehe);
		
			//die Breite jeweils erhöhen
			breite += 1;
			hoehe += 1;
			
			//wenn die Breite bzw. Höhe des Kreises größer ist als
			//die Breite bzw. Höhe des JPanels, dann wird der Timer 
			//gestoppt
			if (breite > getWidth() || hoehe > getHeight())
				timer.stop();

			
			System.out.println(breite);
	
			}
			
		}
		
		//der Konstruktor
		public AnimationKreis () {
			//die Größe des JPanels setzen
			setSize(500,500);
			
			//eine neue Instanz von BufferedImage erstellen
			bild = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
			//den Grafikkontext beschaffen
			Graphics g = bild.getGraphics();
			//die Vordergundfarbe setzen
			g.setColor(Color.WHITE);
			//ein gefülltes Rechteck erstellen
			g.fillRect(0,0,getWidth(),getHeight());
			//den Grafikkontext wieder frei geben
			g.dispose();
		
			//den timer ertellen
			timer = new Timer (1, new TimerListener());
			//Wiederholungen sind erlaubt
			timer.setRepeats(true);
			//den Timer starten
			timer.start();
		}
		
		//die Überschriebenen Methoe paintComponent
		@Override
		public void paintComponent (Graphics g) {
			
			super.paintComponent(g);
			//das Bild zeichnen
			g.drawImage(bild,0,0,this);
		}
		
		//die Methode, welche die Kreise zeichnet
		//als Parameter werden die Koordinaten der linken oberen Ecke
		//sowie die breite und höhe übergeben
		public void paintOvals(int x, int y, int breite, int hoehe) {
			
			//den Grafik-Kontext beschaffen
			Graphics g = bild.getGraphics();
			
			//die Vordergrundfarbe setzen
			g.setColor(Color.BLACK);
			//den Kreis zeichnen
			g.drawOval(x,y,breite,hoehe);
			//den Grafik-Kontext wieder frei geben
			g.dispose();
			//
			repaint();	
		}
}

Das Programm erfüllt seinen Zweck, sprich es zeichnet alle x Millisekunden einen neuen Kreis bis das Ende der Zeichenfläche erreicht wird.

Was sagt ihr zu dem Quelltext?

Ist der so in Ordnung oder sind Fehler enthalten?

Vielen Dank!

Viele Grüße
 

Quaxli

Top Contributor
Dann fange ich doch mal an :D

1. Es wäre schön, wenn Du die Fensterklasse noch reingebastelt hättest, damit man es gleich ausführen und testen kann.

2. Das Graphics-Objekt Deines BufferedImage solltest Du mit createGraphics() abholen, nicht mit getGraphics() - ist auch so in der API erwähnt

3. Ein Timer mit 1 ms halte ich für übertrieben, 10 ms tun es auch.

4. Die Grafik ist noch etwas "unschön". Probier mal das:

Java:
	public void paintOvals(int x, int y, int breite, int hoehe) {

		Graphics g = bild.createGraphics();
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setColor(Color.BLACK);
		g2.drawOval(x, y, breite, hoehe);
		g2.dispose();

		repaint();
	}

5. Der repaint()-Aufruf gehört meiner Ansicht nach in die actionPerformed-Methode(). Scheint mir persönlich schlüssiger. ;)


So, das war mein Senf zum Code. :) Im Großen und Ganzen hast Du das aber ganz gut hinbekommen.
 

Mane123

Bekanntes Mitglied
Hallo,

vielen vielen Dank für Deine Hinweise!!

Zu 1:

Hier der Code :)

Java:
import javax.swing.JFrame;


public class Animation extends JFrame {
	
	Animation(String titel) {
		//den Titel setzen
		super(titel);
		//die Animation einfügen
		add(new AnimationKreis());
		//die Größe festlegen und das Standardverhalten festlegen
		setSize(500,500);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);
	}

}


Zu 2:

Danke für den Hinweis, in meinem Lehrbuch wird in allen Beispielen getGraphics() verwendet.
Nur wenn man explizit Methoden der Klasse Graphics2D benötigt, sollte man den Grafik-Kontext hier umwandelnt.

Wenn man die Methode createGraphics() aufruft, erhält man dann nicht als Rückgabewert eine Instanzvariable von der Klasse Graphics2D?

Dann wäre ja die Umwandlung überflüssig, sondern es sollte ja gleich Graphics2D g2 = bild.createGrapics() lauten?

Java:
        Graphics g = bild.createGraphics();
        Graphics2D g2 = (Graphics2D) g;



Zu 5:

Warum soll denn repaint() in der ActionListener Klasse stehen?

Eine Frage zu repaint():

die Methode repaint() ruft die Methode paint() auf, die Methode paint ruft wiederm folgende Methoden auf:

Swing's implementation of paint() factors the call into 3 separate callbacks:
paintComponent()
paintBorder()
paintChildren()

die paintComponent() ist aber schon die Methode, die ich überschrieben habe?

Viele Grüße
 

Marco13

Top Contributor
Dann wäre ja die Umwandlung überflüssig, sondern es sollte ja gleich Graphics2D g2 = bild.createGrapics() lauten?

Ja.
Graphics2D g2 = bild.createGraphics();

Warum soll denn repaint() in der ActionListener Klasse stehen?
Spätestens wenn es neben paintOvals auch noch 100 andere Methoden gibt, die alle irgendwas painten, und/oder vielleicht die paintOvals-Methode 1000 mal mit verschiedenen Werten aufgerufen wird, sollte man nicht 1000 mal repaint aufrufen, sondern EINmal am ende sagen: So, jetzt mach' aber mal....

Mehr macht man mit "repaint" nämlich auch nicht:
die Methode repaint() ruft die Methode paint() auf

Nicht ganz: repaint bewirkt, dass Swing versucht, "so bald wie möglich" die paint-Methode auszuführen.


die paintComponent() ist aber schon die Methode, die ich überschrieben habe?
Ja, und durch das super.paintComponent(g) wird in der überschriebenen Methode alles gemacht, was auch vorher gemacht wurde... und noch ein bißchen mehr.
 

Mane123

Bekanntes Mitglied
Danke für die Tipps!

Funktioniert das Zeichnen der Kreise so auch ohne BufferedImage als Zeichenfläche, sprich ich möchte gleich in das Panel zeichnen?

Kann ich in der Methode ActionPerformed () den Grafik-Kontext des JPanels aufrufen?

Java:
	class TimerListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			// TODO Auto-generated method stub
			//Koordinaten für die linke obere Ecke
			int x, y;	
			
			//den Grafikkontext des Bildes beschaffen und überschreiben
			Graphics g = bild.getGraphics();
			g.fillRect(0,0,getWidth(),getHeight());

Wenn ich Graphics g = this.getGraphics() aufrufen möchte, dann funktioniert das nicht, da sich die Referenz this auf die Klasse TimerListener bezieht.
Gibt es eine andere Möglichkeit, hier auf die Zeichenfläche des JPanles zuzugreifen?

Viele Grüße
 

Quaxli

Top Contributor
Klar kannst Du direkt ins JPanel zeichnen:

Java:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

//Die Klasse Animation Kreis stellt den größer werdenen Kreis dar
public class AnimationKreis extends JPanel {

	private Timer					timer;

	int	breite	= 100;
	int hoehe = 100;
	JFrame frame;

  public static void main(String[] args){
  	new AnimationKreis();
  }

	class TimerListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {

			// die Breite jeweils erhöhen
			breite++;
			hoehe++;

			if (breite > getWidth() || hoehe > getHeight()){
				timer.stop();
			}

			repaint();
		}

	}

	// der Konstruktor
	public AnimationKreis() {
		
		setPreferredSize(new Dimension(500, 500));

		frame = new JFrame("fenster");
		frame.setLocation(100,100);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(this);
		frame.pack();
		frame.setVisible(true);
		
		
		// den timer ertellen
		timer = new Timer(5, new TimerListener());
		timer.setRepeats(true);
		timer.start();
	}

	// die Überschriebenen Methoe paintComponent
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g2.setColor(Color.BLACK);
		g2.drawOval((getWidth()-breite)/2, (getHeight()-hoehe)/2, breite, hoehe);
		g2.dispose();
	}

}
 

Mane123

Bekanntes Mitglied
Noch eine kleine Frage:

Wenn ich folgende Methode ausführen lasse:

Java:
			if (breite > getWidth() || hoehe > getHeight()) {
				System.out.println(getWidth());
				System.out.println(getHeight());
				timer.stop();
			}
                                      }

Warum liefert dann getWidth() und getHeight() nicht jeweils 500, sondern andere Werte?
Das müssten ja die Breite und Höhe des JPanels sein?

Viele Grüße
 

Marco13

Top Contributor
setSize bringt nicht viel. Die tatsächliche Größe wird vom LayoutManager der übergeordneten Component bestimmt. In diesem Fall machst du den Frame 500x500 groß, und das Panel in seinem ContentPane kann deswegen (abzuüglich Titelleisten und Rand) nur ca. 490x480 groß sein. Eine Möglichkeit, das Panel genau 500x500 groß sein zu lassen: Statt setSize kann man setPreferredSize(new Dimension(500,500)) auf dem Panel aufrufen, und auf dem Frame dann nicht setSize sondern pack(), dann passt er sich automatisch so an, dass das Panel seine Wunschgröße von 500x500 hat.
 

Mane123

Bekanntes Mitglied
Den Vorschlag hatte ich auch schon einmal verucht, aber das funktioniert nicht korret:

Java:
public AnimationKreis () {
			//die Größe des JPanels setzen
			setPreferredSize(new Dimension(500,500));
			
			//eine neue Instanz von BufferedImage erstellen
			bild = new BufferedImage(getWidth(),getHeight(), BufferedImage.TYPE_INT_RGB);
			//den Grafikkontext beschaffen
			Graphics g = bild.getGraphics();

Wenn ich das so erstelle, dann erscheint beim erstellen einer Instanz von BufferedImage eine Fehlermeldung, dass getWidth() und get Height() nicht <= 0 sein dürfen.

Liegt das am Objekt Dimension?

Wie funktioniert das dann?

Viele Grüße
 
G

Gast2

Gast
die Größe des Panels ändert sich erst wenn es "gelayoutet" wird.
In deinem Beispiel ist die Größe nach dem setzen der PrefferedSize immer noch 0. Du kannst mal versuchen nach dem setzen der preferredsize nen doLayout() zu machen, das sollte dafür sorgen dass die Größen gesetzt werden.
 

Mane123

Bekanntes Mitglied
Danke, das hat aber nicht funktioniert.

Die getHeight() liefert immer noch 0.

Bei setSize(500,500) hat es geklappt. Bei setPreferredSize funktioniert es nicht mehr :/

Viele Grüße
 

Mane123

Bekanntes Mitglied
Hier noch mal die Klasse mit dem JFrame:

Java:
import javax.swing.JFrame;


public class Animation extends JFrame {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 9222787341606701434L;

	Animation(String titel) {
		//den Titel setzen
		super(titel);
		//die Animation einfügen
		add(new AnimationKreis());
		//die Größe festlegen und das Standardverhalten festlegen
		setResizable(false);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		pack();
		setVisible(true);
	}

}

Das funktioniert nicht:/
 

Marco13

Top Contributor
In diesem Fall könnte man das ganze ja einfach durch
Java:
final int width = 500;
final int height = 500;
setPreferredSize(new Dimension(width, height));
bild = new BufferedImage(width,height, BufferedImage.TYPE_INT_RGB);
erstellen. Am Anfang zusätzlich zu setPreferredSize auch noch setSize aufzurufen wäre auch eine Option. In jedem Fall solltest du dir überlegen, was passiert, wenn die Größe sich ändert (z.B. in der paintComponent die aktuelle Größe und die Bildgröße vergleichen, und falls nötig ein neues Bild erstellen
Code:
if (bild == null || bild.getWidth() != getWidth() || bild.getHeight() != getHeight());
{
    bild = new BufferedImage...
}
dann könnte die Erstellung des Bildes auch aus dem Konstruktor raus.
 

Mane123

Bekanntes Mitglied
Danke für die Hinweise.

Ich habe das jetzt wie folgt geändert:

Java:
		//der Konstruktor
		public AnimationKreis () {
			
			final int width = 500;
			final int height = 500;
			
			//dife Größe des JPanels setzen
			setPreferredSize(new Dimension (width,height));
			setSize(width,height);
		
			
			//eine neue Instanz von BufferedImage erstellen
			bild = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

Nun funktioniert das alles.

Aber was bewirtk denn setPreferredSize() genau? Warum muss ich dann setSize() auch noch aufrufen?
Wenn ich nur setSize(int,int) erstelle, dann erscheint nur ein gaaanz kleines Fenster. Sobald ich es allerdings wie oben beschrieben erstelle, klappt es wunderbar.

In der API steht ja geschrieben:

public void setPreferredSize(Dimension preferredSize)

Sets the preferred size of this component to a constant value. Subsequent calls to getPreferredSize will always return this value. Setting the preferred size to null restores the default behavior.

public void setSize(int width, int height)

Resizes this component so that it has width width and height height.

Wo ist denn da der Unterschied?
Bedeutet es bei setPreferredSize() dass versucht wird, die gewünschte Größe umzusetzen?

Viele Grüße
 

Marco13

Top Contributor
Die PreferredSize wird vom LayoutManager verwendet. Wenn man z.B. ein Panel hat, und das Panel ein FlowLayout hat (das, vereinfacht gesagt, Components einfach der Reihe nach anordnet, wenn Platz dafür ist), und man legt dann einen Button in dieses Panel, dann fragt das FlowLayout den Button "Wie ist deine PreferredSize?", der Button gibt seine Wunschgröße zurück, und das FlowLayout verwendet (grob gesagt) setSize, um ihm diese Größe dann tatsächlich zu geben. Wenn der Button aber sagt, dass er gerne 100x100 groß wäre, aber in dem Panel nur 50x50 Platz ist, dann wird der Button halt kleiner gemacht, als er eigentlich sein will. Wenn man das Panel fragt, wie SEINE PreferredSize ist, sagt es "Och, ich hab hier einen Button in mir, der gerne 100x100 groß wäre - deswegen wäre ich auch gerne 100x100 groß".
Da wollte ich schon ewig mal einen Blog/FAQ-Eintrag dazu schreiben.
Wie auch immer: Dass das Fenster ohne setPreferredSize so klein wird, liegt daran, dass ein JPanel (wenn nichts drin ist) erstmal eine PreferredSize von 0x0 hat - und die bekommt es dann auch.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
S Animation/links-rechts Java Basics - Anfänger-Themen 9
G Animation Timer Nano Time Java Basics - Anfänger-Themen 2
J Animation eines Körpers auf der schiefen Ebene Java Basics - Anfänger-Themen 11
Anica Processing Animation Bild-Position ändern Java Basics - Anfänger-Themen 9
D Animation JPanel Java Basics - Anfänger-Themen 1
WetWer Animation JLabel Java Basics - Anfänger-Themen 2
A Animation einer Zahnradpumpe Java Basics - Anfänger-Themen 6
B Animation mit Thread(s) Java Basics - Anfänger-Themen 23
K Möglichkeiten um eine einfache Animation darzustellen Java Basics - Anfänger-Themen 7
C Animation nur einmal durchführen Java Basics - Anfänger-Themen 3
N Bilderarray als animation ablaufen lassen?? Java Basics - Anfänger-Themen 6
G Threads SuM Class Animation Java Basics - Anfänger-Themen 13
J .gif Animation Java Basics - Anfänger-Themen 4
J Animation Java Basics - Anfänger-Themen 5
M Animation in einem Multiplayerspiel Java Basics - Anfänger-Themen 3
S Animation nach bestimmter Zeit ausführen Java Basics - Anfänger-Themen 3
T Animation Java Basics - Anfänger-Themen 2
U Y-Animation Java Basics - Anfänger-Themen 3
K Animation: Ball fällt herunter Java Basics - Anfänger-Themen 2
G Zufalls Animation Java Basics - Anfänger-Themen 9
Luk10 einfach animation Java Basics - Anfänger-Themen 2
H Animation malen! Java Basics - Anfänger-Themen 2
S JTable mit Animation??? Java Basics - Anfänger-Themen 7
B Flash Animation SWF in Java absielen Java Basics - Anfänger-Themen 18
R Probleme mit Animation und der erstellung einer jar Datei. Java Basics - Anfänger-Themen 11
U Animation bleibt leer (weiß) Java Basics - Anfänger-Themen 5
T Animation übermalt panels Java Basics - Anfänger-Themen 6
M Simple Animation funktioniert nicht (JFrame, Canvas, Thread) Java Basics - Anfänger-Themen 6
S Desktop Animation Java Basics - Anfänger-Themen 15
T Abspeichern einer Animation in *.bmp Java Basics - Anfänger-Themen 12
K animation in applet, thread problem (?) Java Basics - Anfänger-Themen 3
K Animation warten lassen Java Basics - Anfänger-Themen 9
F Animation Java Basics - Anfänger-Themen 3
J Animation Java Basics - Anfänger-Themen 2
N Methoden Rekursion mit Kreisen Java Basics - Anfänger-Themen 7
P Verbindung von Zwei Kreisen löschen ! Java Basics - Anfänger-Themen 6
H Grafikaufgabe mit Linien und Kreisen Java Basics - Anfänger-Themen 5
TheKing Objekt um Punkt kreisen lassen Java Basics - Anfänger-Themen 7

Ähnliche Java Themen

Neue Themen


Oben