Swing JButton flackert trotz DoubleBufferings

Bjoern82

Mitglied
Hallo liebe Java-Community,

leider habe ich ein Problem mit den JButtons und hoffe, dass es eine leichte Lösung gibt.

Ich habe ein animiertes Applet erstellt und arbeite mit DoubleBuffering, um Flimmern zu unterdrücken, was auch prima funktioniert. Bisher habe ich allerdings alle GUI-Elemente wie Buttons einfach "von Hand" gezeichnet und die Koordinaten abgefragt, was sehr unprofessionell ist. Deswegen wollte ich nun echte Buttons verwenden. Mit den älteren AWT Buttons hatte ich leider das Problem, dass sie stark geflimmert haben, denn AWT unterstützt anders als Swing noch kein DoubleBuffering für seine Komponenten. Somit bin ich nun auf JButtons umgestiegen und habe mit .setDoubleBuffered(true) DoubleBuffering aktiviert. Mein Problem: Auch die JButtons flimmern, trotz des aktivierten DoubeBufferings.

Kennt jemand die Ursache bzw. weiß jemand die Lösung für dieses Problem?

Schon einmal vielen Dank im Voraus!

Viele Grüße
Björn
 
Kleines Update: Ich habe nun noch ein wenig weiter probiert, mein Problem zu lösen, leider ohne Erfolg. Ich hatte die Idee das repaint der Buttons zu unterdrücken und sie statt dessen selbst über .paint() zu malen. Die Funktion .setIgnoreRepaint(true) blieb leider wirkungslos, dafür konnte ich die Buttons mit .setOpaque(false) verschwinden lassen. Mit den Funktionen .paint(g), .update() oder .print() klappt es nun tatsächlich die Buttons zu malen und(!) sie flackern auch nicht mehr, ... nur sind sie anschließend leider immer in der linken oberen Ecke, egal welche JButton-Koordinaten ich festlege. Die Funktion .setBounds(x, y, w, h) bleibt wirkungslos. Zur Info: Die flackernden Buttons waren an der richtigen Position. Für die hat das Festlegen der Koordinaten also wunderbar geklappt.

Ich würde mich sehr über eine Antwort freuen!

Gruß
Björn
 
Zuletzt bearbeitet:
Kannst Du mal ein kurzes komplierbares Bsp mit den animierten JButtons posten?
Swing ist standardmäßig doublebuffered, es muss nicht explizit aktiviert werden.
 
Kannst Du mal ein kurzes komplierbares Bsp mit den animierten JButtons posten?
Swing ist standardmäßig doublebuffered, es muss nicht explizit aktiviert werden.

Gerne: Hier ist also eine abgespeckte Version von meinem Programm, welches nur die Teile enthält, die direkt mit meinem Problem zusammen hängen:

dieses bsp ist nicht kompilierbar, da sind klassen von dir dabei und syntaxfehler! :/

Entschudigung, du hast Recht! Ich hatte meinen Code auf die Schnelle abgespeckt und dabei ein paar Dinge vergessen. Unten ist nun eine korrigierte, lauffähige Version meines Codes.
 
Zuletzt bearbeitet:
Hier die korrigierte Version meine Quellcodes:

Java:
import java.applet.Applet;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Rectangle2D.Double;

import javax.swing.JButton;

public class MyClass extends Applet implements Runnable
{
	private static final long serialVersionUID = 1L;
	Thread animator;
	Dimension offDimension;
	Image offImage;
	Graphics2D offGraphics;
	private double scale;
	private Shape offscreenClip;
	private Dimension virtualDimension;
	private Color bg;
	int delay;
	
    
    JButton my_button = new JButton();
	
	public void init()
	{
		virtualDimension = new Dimension(981, 460);
		offscreenClip = new Rectangle2D.Double(0,0,virtualDimension.getWidth(),virtualDimension.getHeight());
		
		setBg(Color.black);
		setLayout(null);
		
		String str = getParameter("fps");
		int fps = (str != null) ? Integer.parseInt(str) : 10;
		delay = (fps > 0) ? (220 / fps) : 100;
						
		my_button = new JButton("Beschriftung");
		my_button.setBounds( 23, 120, 213, 32 );
		my_button.setBackground(Color.black);
		my_button.setForeground(MyColors.Orange);
		my_button.setFont(new Font("Dialog", Font.PLAIN, 15));
		//my_button.setDoubleBuffered(true);
		//my_button.setOpaque(false);
		//my_button.setIgnoreRepaint(true);		
	    add(my_button);	
	}
		
	public void paintFrame(Graphics g)
	{
		Graphics2D g2d = (Graphics2D) g;	
			
		//startscreen_button[0][0].setOpaque(true);
		//startscreen_button[0][0].setLocation(100, 100);
		g2d.setPaint(Color.green);
		g2d.drawRect(13, 110, 233, 52);
		my_button.paint(g2d);	
		//startscreen_button[0][0].setOpaque(false);
		
	}
	
	public void run()
	{
		long tm = System.currentTimeMillis();
		while(Thread.currentThread() == animator)
		{
			repaint();
			try
			{
				tm += delay;
				Thread.sleep(Math.max(1, tm - System.currentTimeMillis()));
			}
			catch(InterruptedException e)
			{
				break;
			}
		}
	}
	
	private void setBg(Color color)
	{
		bg = color;
	}
	
	public void start()
	{
		start(this);
	}
	
	public void start(MyClass myClass)
	{
		animator = new Thread(this);
		animator.start();
	}
	
	public void stop()
	{
		animator = null;
		offImage = null;
		offGraphics = null;
		Audio.bg1.stop();
		Audio.bg2.stop();
		Audio.bg3.stop();
	}

	public void update(Graphics g)
	{
		Dimension size = getSize();
		double scaleX = size.getWidth() / virtualDimension.getWidth();
		double scaleY = size.getHeight() / virtualDimension.getHeight();
		scale = Math.min(scaleX, scaleY);
		scale_buttons(my_button, scale);
		if(offGraphics == null || !offDimension.equals(size))
		{
			offImage = createImage((int)size.getWidth(), (int)size.getHeight());
			offGraphics = (Graphics2D) offImage.getGraphics();
			offGraphics.scale(scale, scale);
			offDimension = size;			
			offGraphics.setRenderingHint(	RenderingHints.KEY_ANTIALIASING,
                    						RenderingHints.VALUE_ANTIALIAS_ON);
		}		
		Double rect = new Rectangle2D.Double(0,0,size.getWidth()/scale,size.getHeight()/scale);
		offGraphics.setColor(Color.black);
		offGraphics.setClip(null);
		offGraphics.fill(rect);
		offGraphics.setClip(offscreenClip);
		offGraphics.setColor(bg);
		offGraphics.fill(rect);
		paintFrame(offGraphics);
		g.drawImage(offImage, 0, 0, null);
	}
	
	void scale_buttons(JButton my_button, double scale)
	{
		my_button.setBounds(	(int)Math.round(scale*(23)),
								(int)Math.round(scale*(120)),
								(int)Math.round(scale*213),
								(int)Math.round(scale*32));
		my_button.setFont(new Font("Dialog", Font.PLAIN, (int)(15*scale)));
	}
}

Was man sieht:
Der Aufruf von .paint() malt zwar einen nicht flimmernden Button, der aber an der falschen Position erscheint. Der richtige Button flimmert dagegen. Wer weiß Rat?
 
Zuletzt bearbeitet:
Kannst Du mal kurz beschreiben was genau Du vorhast. Aus dem Code werde ich nicht schlau.
Was soll wie animiert werden?

Ich programmiere ein Spiel in einem Applet. (Der Übersichtlichkeit wegen habe ich aber alles, was sich nicht direkt auf die Buttons bezieht raus gelassen). Animiert wird also der Hintergrund. Bisher habe ich auch die Buttons, die ich brauche, alle selbst gemalt, was aber doch eher unprofessionell ist, zumal es ja in Java bereits vorgefertigte Klassen für Buttons gibt. Ich will also GUI-Elemente wie Buttons auf die Bildoberfläche setzen. Das klappt auch, nur flimmern die dann furchtbar, was sehr schlecht aussieht.
Es klappt auch die Buttons mit dem Aufruf von my_button.paint(g) selbst zu zeichnen, dann flimmern die Buttons nicht mehr, dafür werden sie dann aber prinzipiell in die linke obere Ecke gesetzt, egal was bei .setBounds eingestellt wurde. In meinem Beispielprogramm ist das zu sehen. Vielen Dank schon mal im Voraus!
 
Moin,

ins Blaue, mindestens:

Java:
extends Japplet

Gruß,
Fancy
 
Weiss jetzt nicht was Du genau vorhast, aber wenn es nur um ein anklickbares Element geht, muss man ja nicht unbedingt einen Button nehmen. Bei einem Spiel würde ich das meiste sowieso selbst zeichnen - ausser ich benötige den vollen Funktionsumfang eines Buttons.

Grundsätzlich sollte man AWT und Swing Komponenten nicht mischen --> also wenn JButton, dann auch JApplet.
Zum zeichnen würde ich nicht direkt auf das Applet malen, sondern eine separate Komponente verwenden und diese ins Applet rein setzen. Das überschreiben von paint oder update kann Probleme verursachen und ist in den meisten Fällen nicht notwendig.
Hier mal ein simples Bsp, wie ich da vorgehen würde. Gezeichnet wird auf einem JPanel in der überschriebenen paintComponent():
Java:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JPanel;

public class MyClass extends JApplet {
	private AnimatorPanel anim;
	private boolean loop;

	public void init() {
		anim = new AnimatorPanel();
		this.setBackground(Color.BLACK);
		this.getContentPane().add(anim);
	}

	public void start() {
		loop = true;
		new Thread(anim).start();
	}
	
	public void stop() {
		loop = false;
	}

	class AnimatorPanel extends JPanel implements Runnable {
		private int degree;

		public AnimatorPanel() {
			this.setLayout(null);
			JButton button = new JButton("Button");
			button.setBounds(20, 20, 300, 65);
			this.add(button);
			degree = 0;
		}

		public void run() {
			while (loop) {
				try {
					Thread.sleep(40);
					degree++;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				repaint();
			}
		}

		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			Graphics2D g2 = (Graphics2D) g.create();
			int w = this.getWidth();
			int h = this.getHeight();
			g2.rotate(Math.toRadians(degree), w / 2, h / 2);
			g2.setColor(Color.ORANGE);
			g2.setStroke(new BasicStroke(5));
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g2.drawLine(0, 0, w, h);
			g2.dispose();
		}
	}
}
Wenn Du hier den JButton zum flackern bringts, kannst Du ja mal den Code posten.
 

Zurück
Oben