JFrame langsam / seltsames Verhalten

Phteven

Mitglied
Hallo Alle,

habe ein kleines Programm geschrieben, welches ein 500 x 500 Fenster in 50 x 50 JLabels aufteilt, welche alle eine zufällige Farbe haben. Das wird über einen Timer alle halbe Sekunde neu gemacht. Ist als Testprogramm ohne konkreten Nutzen gedacht. (Der Weg ist das Ziel)

Ich hätte erwartet (und auch gewünscht), dass es etwa so ist, als ob ich eine neue Seite in einem Block aufschlage : zack, neue Farben da.

Der Bildaufbau hat irgendwie Bildschirmschoner - Charakter (er baut sich wuselnd und bewegend auf irgendwie). Ich hätte gerne, dass es schneller geht, sehe aber momentan nicht wie das gehen soll.

Bei 128 x 128 Felder etwa dauert der aufbau schon fast 3 Sekunden?

Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

public class simpleGui {
	private int size = 50;
	private JFrame frame = new JFrame("Titel hier!");
	private JPanel panel1 = new JPanel();
	private GridLayout layout = new GridLayout(size,size);


	simpleGui(){
		initGui();
	}
	
	public void initGui(){
		System.out.println("Initialisiere Gui...");
		frame.setSize(500,500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		panel1.setLayout(layout);
		
		ActionListener action = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) 
			{
				panel1.removeAll();
					
				for (int i = 0; i < size; i++) 
					for (int j = 0; j < size; j++) 
						panel1.add(new Brick(""), null);
					
				
				frame.add(panel1, BorderLayout.CENTER);
				frame.setVisible(true);
				}
			
		};
		
		

		frame.add(panel1, BorderLayout.CENTER);
		frame.setVisible(true);
		
		
		Timer t = new Timer(500,action);
		t.setRepeats(true);
		t.start();
		
	}

	public static void main(String[] args) {
		simpleGui sg1 = new simpleGui();
	}
	
	class Brick extends JLabel{
		
		//random color
		private Color color = new Color((int) (Math.random()*255),(int) (Math.random()*255),(int) (Math.random()*255));
		
		public Brick(String s){
			super(s);
			this.setBackground(color);
			this.setOpaque(true);
//			System.out.println("i am " + color.toString());
		}

	}
}
 
hast du es schon mal ausgeführt? die 500 x 500 sind die Pixelgröße vom Frame. Angezeigt werden tatsächlich size*size Objekte, das kann man ändern wie man will.


10 lese ich auch nirgends, wie kommst du auf diese Zahl?
 
Zuletzt bearbeitet:
Bei 128 x 128 Felder etwa dauert der aufbau schon fast 3 Sekunden?
Das sind statt der 50x50 = 2500 Felder, die in deinem Codeschnipsel stehen, 128x128 = 16384 JLabels, also mehr als 6x soviel.
Mich wundert es nicht, daß das Neuerzeugen von 16000 Komponenten, das adden zu einem JPanel, dann das adden des JPanels zu einem JFrame und schliesslich das anzeigen des JFrames im Bereich von mehreren Sekunden liegt.

Um das Programm zu beschleunigen, musst du alle unnötigen Operationen unterbinden:
Packe alle JLabels in ein Array JLabel[size*size], dann ab damit ins JPanel und das JPanel ab in den JFrame. In deiner actionPerformed() musst du einfach über das Array gehen, für jedes Element eine Zufallsfarbe bestimmen, und dann mit dieser Farbe den background neu setzen.

Es ist überflüssig, die JLabels jedesmal von Grund auf neu zu erzeugen und den JPanel neu aufzubauen. Dies ist nur notwendig, wenn sich size ändert. Deine Brick-Klasse kannst du dir eigentlich auch sparen, weil du den background und die opacity auch direkt beim JLabel setzen kannst. Schliesslich ist es unnötig, ein- und dasselbe JPanel immer wieder auf die content pane des JFrames zu packen. Dort befindet es sich nämlich nach dem ersten Durchlauf schon längst.

Ich habe mal sowas ähnliches gemacht, allerdings habe ich zum Anzeigen von verschiedenfarbigen Quadraten eine JTable genommen. Das ganze wurde ab ca. 10000 Zellen auch ziemlich unperformant. Ich habe mir dann eine eigene JComponent geschrieben, die die mehreren zehntausend Quadrate direkt auf einen Graphics2D pinselt, also ohne den ganzen Overhead der Erzeugung von ein paar zehntausend Komponenten. Dann lief die ganze Sache auf einmal sehr flüssig.
 
Ist diese Umsetzung das was du meintest?
Java:
package de.johvil.ss2014.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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

public class simpleGui {
	private int size = 30;
	private JFrame frame = new JFrame("Titel hier!");
	private JPanel panel1 = new JPanel();
	private GridLayout layout = new GridLayout(size, size);
	private Brick[][] fields = new Brick[size][size];

	simpleGui() {
		initGui();
	}

	public void initGui() {
		System.out.println("Initialisiere Gui...");
		frame.setSize(1000, 1000);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		panel1.setLayout(layout);

		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				fields[i][j] = new Brick("");
				panel1.add(fields[i][j]);
			}
		}

		ActionListener action = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {

				for (int i = 0; i < size; i++)
					for (int j = 0; j < size; j++)
						fields[i][j].newColor();
			}

		};

		frame.add(panel1, BorderLayout.CENTER);
		frame.setVisible(true);

		Timer t = new Timer(1000, action);
		t.setRepeats(true);
		t.start();

	}

	public static void main(String[] args) {
		simpleGui sg1 = new simpleGui();
	}

	class Brick extends JLabel {

		// random color
		private Color color = new Color((int) (Math.random() * 255),
				(int) (Math.random() * 255), (int) (Math.random() * 255));

		public Brick(String s) {
			super(s);
			this.setBackground(color);
			this.setOpaque(true);
			// System.out.println("i am " + color.toString());
		}

		public void newColor() {
			color = new Color((int) (Math.random() * 255),
					(int) (Math.random() * 255), (int) (Math.random() * 255));
			this.setBackground(color);
		}

	}
}

es geht jetzt auch bei größeren Zahlen genauso flott wie bei kleineren (20 - 150 ausprobiert) aber trotzdem ist der "Bildaufbau" nicht auf einen Schlag sondern eher ... wuselig. Ist das nur bei mir so?


Hintergrund:
Eigentlich wollte ich eine Art "Life-Game" programmieren, also sagen wir mal ein 30 * 30 Feld auf dem Gras wächst und da laufen Hasen rum die das Gras wegfuttern. Diese Oberfläche hier sollte als graphische Anzeige dienen.

Bin ich da mit dem JLabel als Repräsentant für ein Feld ohnehin falsch? Wäre etwas anderes passender / performanter? Oder gleich diesen SceneBuilder benutzen?

Ich will nur ein quadratisches Feld haben, dessen einzelne Felder will ich mit einer Farbe, optional mit einem kleinen Bildchen belegen. Das ganze soll aber dynamisch sein, deswegen der Timer. Wenn z.B. ein Tag um ist, hat sich der Hase eben woanders hinbewegt und sein Bildchen soll das auch machen.
 
Also bei mir läuft dein Beispiel einwandfrei, eben ruckartiges verändern der Farben.

Vielleicht bringt es ja was alles noch mit repaint(); danach aufzurufen. Also bei mir sehe ich keinen großen Unterschied da es auch so schon funktioniert.
Java:
ActionListener action = new ActionListener() {
 
            @Override
            public void actionPerformed(ActionEvent e) {
 
                for (int i = 0; i < size; i++)
                    for (int j = 0; j < size; j++) {
                        fields[i][j].newColor();
                        fields[i][j].repaint(); // Neu zeichnen
                     
            }
            }
        };
 
Ein Arbeitskollege, welchen ich für sehr qualifiziert in diesem Bereich halte, hat mir gleich von awt abgeraten und gemeint ich solle doch Java FX benutzen.

Habe ein ähnliches Programm mit den FX Komponenten gemacht (Bilbliothek einbinden).

Was soll ich sagen? Bildaufbau ist auch bei 1000x1000 Feldern sofort! Den Ticker kann ich jede 100ms einstellen und das funktioniert immer noch auf einen Schlag. Zusätzlich kann ich direkt auf den Bildschirm ausgeben, ohne ein Fenster oder ähnliches.

Danke für alle hilfreichen Anregungen.
 

Zurück
Oben