Problem mit Doppelpufferung

Hemme

Mitglied
Hi,

Ich wollte ein wenig in die Grafikprogrammierung einsteigen und hab mir folgendes Programm gebastelt.
Ein Kreis wandert einfach über das Fenster.
Jetzt wollte ich mit der Doppelpufferung verhindern, dass der Kreis "nachzieht"..
Ich habe Beispiele im Netz gefunden, aber ich verwende sie anscheinend falsch.

Java:
import javax.swing.*;
import java.awt.*;
import java.util.*;

public class KreisZeichnen extends JFrame implements Runnable {

	int xPos = 50;
	int yPos = 50;
	
	// Variablen zur Doppelpufferung
	private Image dbImage;
	private Graphics dbg;
	
	public KreisZeichnen() {
		
		gui();
		Thread th = new Thread(this);
		th.start();
	}
	
	public void gui() {
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(600,600);
		setVisible(true);
		
	}

	public void run() {
	
		while (true) {
			
			xPos++;
			yPos++;
			
			repaint();
			
			try {
				
				Thread.sleep(20);
				
			} catch (InterruptedException e) {
			
			}
		}
	}

	// Methode zur Doppelpufferung 
	
	public void update(Graphics g) {
	
		// Initialisierung des DoubleBuffers
		if (dbImage == null) {

      		dbImage = createImage (this.getSize().width, this.getSize().height);
      		dbg = dbImage.getGraphics();
      	}

		// Bildschirm im Hintergrund löschen
		dbg.setColor (getBackground());
		dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);

		// Auf gelöschten Hintergrund Vordergrund zeichnen
		dbg.setColor(getForeground());
		paint(dbg);

		// Nun fertig gezeichnetes Bild Offscreen auf dem richtigen Bildschirm anzeigen
		g.drawImage(dbImage, 0, 0, this);
		
	}
	
	public void paint(Graphics g) {
		
		g.drawOval(xPos,yPos,100,100);
	}
	
	public static void main(String[] args) {
		
		KreisZeichnen zeichnen = new KreisZeichnen();
		
	}
}

Kann mir jemand sagen, was daran falsch ist?
 
Java:
    public void paint(Graphics g) {
        // hier super.paint(g);  <---
        g.drawOval(xPos,yPos,100,100);
    }
(oder besser JPanel + paintComponent. Mehr dazu und auch zu Swings Doppelpufferung hier nachzulesen: Painting in AWT and Swing)
 
1. Nicht auf einem Top-Level Container zeichnen.
2. das Verhalten der Doppelpufferung ist in Swing doch sehr komplex, man sollte nicht damit rumspielen finde ich 🙂
3. Wenn du aus einem anderen Thread heraus auf primitive Typen einer Swing Komponente zugreifst, dann versehe diese Variablen mit dem Schlüsselwort "volatile"
4. "repaint()" aus einem anderen Thread heraus aufzurufen ist nicht klug, lieber mit "EventQueue.invokeLater(runnable)" oder "SwingUtilities.invokeLater(runnable)" dann einfügen. Wenn du den Zugriff auf die primitiven Typen dort ebenfalls änderst, dann kannst du dir das Schlüsselwort "volatile" sparen
5. Wenn du deine Klasse von JPanel erben lässt und den Kram für die Doppelpufferung rausnimmst, dann funktioniert es wunderbar. 🙂
Java:
public class KreisZeichnen extends JPanel implements Runnable {
	 
    int xPos = 50;
    int yPos = 50;
    
    public KreisZeichnen() {
        Thread th = new Thread(this);
        th.start();
    }
 
    public void run() {
        while (true) {
        	EventQueue.invokeLater(new Runnable() {
				@Override public void run() {
					xPos++;
		            yPos++;
		            repaint();
				}
			});
            try { Thread.sleep(20); } catch (InterruptedException e) {}
        }
    }
    
    public void paint(Graphics g) {
        super.paint(g);
        g.drawOval(xPos,yPos,100,100);
    }
    
    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(600,600);
        KreisZeichnen zeichnen = new KreisZeichnen();
        f.add(zeichnen);
        f.setVisible(true);
    }
}

Das Was, Wie und Warum kannst du in den Links nachschlagen, die dir eRaaaa und L-ectron-X gegeben haben. 🙂
 
4. "repaint()" aus einem anderen Thread heraus aufzurufen ist nicht klug, lieber mit "EventQueue.invokeLater(runnable)" oder "SwingUtilities.invokeLater(runnable)" dann einfügen.

Da muss ich dir widersprechen!

http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html hat gesagt.:
The following JComponent methods are safe to call from any thread: repaint(), revalidate(), and invalidate(). The repaint() and revalidate() methods queue requests for the event-dispatching thread to call paint() and validate(), respectively. The invalidate() method just marks a component and all of its direct ancestors as requiring validation.
+
JComponent.repaint() registers an asynchronous repaint request to the component's RepaintManager, which uses invokeLater() to queue a Runnable to later process the request on the event dispatching thread.
 

Zurück
Oben