Swing Performance bei Griderstellung verbessern

Wolfone

Mitglied
Hallo liebe Forumler,

nachdem ich jetzt mal ein wenig das mächtige Internet befragt habe und nur partiell schlauer geworden bin, würde es mich interessieren, ob hier jemand Tips für mich hat.

Problem wie folgt:

Ich bin noch ein Anfänger, was GUIs angeht und benutze Eclipse mit WindowBuilder-PlugIn.
Mein Plan war es das Game of Life zu implementieren. Und zwar in einer dynamischen Variante.
Der User soll vieles selbst angeben können, sodass ein etwas aufwendigeres Interface nötig wurde.

Unter anderem soll die Grid-Größe dynamisch sein.
Meine Idee war es also ein JPanel einzusetzen, dazu das GridLayout zu verwenden und dieses in Abhängigkeit der Usereingabe mit weiteren JPanels zu bestücken, damit ich in vollem Umfang die Farben bearbeiten kann usw.; darunter organisiere ich mein Frame mit MiGLayout . Wenn ich jedoch zur Laufzeit die Spielfeldgröße für einen neuen Durchgang verändere dauert das ab einem bestimmten Punkt (ca. 160x160) ziemlich lange; sprich mehrere Sekunden, was bei meinem Rechner nicht der Fall sein dürfte. Conclusio: ich habe schlecht programmiert bzw. einen schlechten Ansatz gewählt. Es folgt das entsprechende Code-Segment.

Java:
protected void setField(int Zeilen, int Spalten){       //ZeilenSpaltendimension, Vorbereitung der Arrays und Aktualisieren der Zeichnung, Listener hinzufügen
        Zeilenanzahl = Zeilen;
        Spaltenanzahl = Spalten;
        this.removeAll();
        this.validate();
        setLayout(new GridLayout(Zeilen,Spalten,1,1));
        DarstellungsArray = new JPanel[Zeilen][Spalten];
        ZellenArray = new Cell[Zeilen][Spalten];
        for(int i=0;i<=Zeilen-1;i++){
            for(int j=0;j<=Spalten-1;j++){
                DarstellungsArray[i][j] = new JPanel();
                DarstellungsArray[i][j].addMouseListener(new MouseListener(){
                    public void mouseClicked(MouseEvent event){
                        if(clickable==true){
                            ((JPanel) event.getSource()).setBackground(Color.BLUE);                   
                        }
                    }
                    public void mouseEntered(MouseEvent e) {           
                    }
                    public void mouseExited(MouseEvent e) {                       
                    }
                    public void mousePressed(MouseEvent e) {                       
                    }
                    public void mouseReleased(MouseEvent e) {                       
                    }
                });
                this.add(DarstellungsArray[i][j]);
                ZellenArray[i][j] = new Cell();
            }
        }
        this.validate();
        this.repaint();
    }

Ich denke die Variablennamen sind so sprechend gewählt, dass ich diese nicht gesondert erklären muss.
Cell ist ein eigener Datentyp, der ein paar Daten speichert, die für meine Rechnungen und Features von Bedeutung sein sollen.

Habt ihr Ideen, wie das performanter zu gestalten wäre? Evtl. mit weniger Objekt-Instanzierungen? Eine weitere Idee war es, nicht jedem Feld einen Listener hinzuzufügen, sondern nur der darunterliegenden Komponente (also dem JPanel) und sich dann für das Markieren von Feldern zu Spielbeginn die Source des Events zu besorgen, was mir aber immer nur das darunterliegende JPanel liefert.

Ich hoffe der Codeschnipsel ist ausreichend um etwas dazu zu sagen; jedenfalls wäre ich dankbar für Tips!

Beste Grüße,
Wolf
 

strußi

Top Contributor
probier anstelle der einzelen adds mal über eine methode

Code:
/**
     * Interne Methode zum hinzufügen der einzeln Componenten auf das Panel
     * @param gbc GridBagConstraints
     * @param c Component to add
     * @param gridx Colum
     * @param gridy Row
     * @param width count of Colums
     * @param height count of Rows
     * @param weightx percent of the colum for the Component
     * @param weighty percent of the row for the Component
     * @param fill fill orientation
     */
    private void addComponent( GridBagConstraints gbc, Component c, int gridx, int gridy, int width, int height,
                               double weightx, double weighty, int fill){
        gbc.gridx      =gridx;    gbc.gridy     =gridy;
        gbc.weightx    =weightx;  gbc.weighty   =weighty;
        gbc.gridheight =height;   gbc.gridwidth =width;
        gbc.fill =fill;
        add( c, gbc);
    }

das dürfte in deinem Sinne sein, dass du nicht mehr so viel code hast. Dieser Code ist für ein GridBagLayout.
 

Harry Kane

Top Contributor
Schön wäre es ausserdem, wenn der Post bei der Lösung des Problems helfen würde. Ich sehe nicht, wie der TO durch Ersatz der GridLayout durch ein GridbagLayout und Verwendung der addComponent-Methode irgendwas gewinnt. Der TO hat ja offensichtlich vor allem ein Performanceproblem.
Grundsätzlich rate ich davon ab, grafische Ausgaben mit vielen Einzelelementen, die eigentlich keine Gui sind, über einzelne JComponents zu realisieren. Stattdessen sollte man eine einzelnen JComponent verwenden und das grafische Gedöns in einem Rutsch in einer entsprechenden JComponent zeichnen.
Hier habe ich mal was zusammengeklöppelt:
Java:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingawt;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClickBoard {

    private ClickPanel cp;

    public static void main(String[] args) {
        new ClickBoard("Click Panel");
    }

    public ClickBoard(String titel) {
        JFrame frame = new JFrame(titel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        cp = new ClickPanel();
        frame.getContentPane().add(cp);
        frame.pack();
        frame.setVisible(true);
    }

}

class ClickPanel extends JComponent {

    final int COUNT = 10;

    private boolean[][] tiles = new boolean[COUNT][COUNT];

    public ClickPanel() {
        super();
        addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                int x = e.getX();
                int y = e.getY();
                flip(x, y);
          }

          });
      setPreferredSize(new Dimension(300, 300));
    }

    public void flip(int x, int y) {
        Dimension size = getSize();
        int i = Math.min(x * COUNT / size.width, COUNT - 1);
        int j = Math.min(y * COUNT / size.height, COUNT - 1);
        tiles[i][j] = !tiles[i][j];
        repaint();
 
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        Dimension size = getSize();
        g2.setColor(Color.GREEN);
        for (int i = 0; i < COUNT; i++) {
            for (int j = 0; j < COUNT; j++) {
                if (tiles[i][j]) {
                    g2.fillRect(size.width / COUNT * i, size.height / COUNT * j, size.width / COUNT, size.height / COUNT);
                }
            }
        }
        g2.setColor(Color.BLACK);
        for (int i = 1; i < COUNT; i++) {
            g2.drawLine(size.width / COUNT * i, 0, size.width / COUNT * i,    size.height);
            g2.drawLine(0, size.height / COUNT * i, size.width, size.height / COUNT * i);
       }
    }

}
 
Zuletzt bearbeitet:

Wolfone

Mitglied
Sooo, entschuldigt bitte meine späte Antwort. Mein Real-Leben hat mich etwas von meinen Programmierbemühungen abgehalten in den letzten Wochen. Ich werde mir das jetzt mal ansehen!

Danke für eure Antworten!
 

Wolfone

Mitglied
Also, mir gefällt die Lösung von Harry Kane schon gut. Die bietet auf jeden Fall ein gutes Performance-Plus.
Ich steige allerdings glaube ich noch nicht bei allen Details durch.
Bevor ich dazu komme (damit werde ich mich noch ein bisschen selbst beschäftigen bevor ich nachfrage), hätte ich jedoch noch eine andere Sache.

Wenn ich den Count höher mache als 10, sagen wir mal auf 50 gehe, dann wird die Ansteuerung der Cellen sehr unpräzise und mit zunehmendem Count ungenauer, was allerdings nicht sein sollte.
Offensichtlich wird dann ein Rand in das Frame gezeichnet, der nicht dort sein sollte, weswegen logischerweise die relative Bestimmung des Tiles anhand der width und height fehlschlagen muss.

Gibt es da Erfahrungen, woran das liegt und wie dieses Problem zu beheben ist?
 

Meniskusschaden

Top Contributor
Ich denke, es hängt damit zusammen, dass die letzte Spalte breiter sein kann, als die übrigen. Bei der Bestimmung der angeklickten Spaltennummer wird dann so gerechnet, als seien alle Spalten gleich breit. Je weiter man nach rechts kommt, desto größer ist der Abstand zwischen der tatsächlich gezeichneten Trennlinie und der bei gleicher Spaltenbreite "imaginären" Trennlinie. Wahrscheinlich ist folgende Rechnung präziser:
Java:
// int i = Math.min(x * COUNT / size.width, COUNT - 1);
int cellWidth = size.width/COUNT;
int i = Math.min(x/cellWidth, COUNT-1);
 

Wolfone

Mitglied
Ahhhh, ok, das ist schonmal präziser! Danke für deine schnelle Antwort.
Hast du Erfahrung damit, wie man das in der Darstellung vereinheitlichen kann? Das funktioniert zwar so, aber es sieht halt nicht gerade doll aus ab einer gewissen Zellenanzahl.


EDIT: wenn ich so drüber nachdenke wundert es mich etwas, dass das genauer ist.

Du bestimmst ja erst die theoretische Zellenbreite. Müsste ich nicht theoretisch trotzdem Fehler bekommen, wenn die letzte Zelle doch breiter ist? Diese nimmt ja dann unverhältnismäßig viel von der Gesamtbreite ein, sodass es eigentlich einen gewissen prozentualen Versatz geben müsste (sprich ähnliches Problem wie das von dir geschilderte).

Ohje...ich hoffe das war verständlich -.-
 
Zuletzt bearbeitet:

Thallius

Top Contributor
Ich finde die Lösung mit einem Panel pro Feld nicht sehr glücklich. Mach lieber ein Panel und zeichne dann in paintComponent() die Felder selber.
 

Wolfone

Mitglied
Jo, das versuche ich inzwischen auf Anraten von Harry Kane, da mir das auch besser gefällt, sowohl strukturell als auch performancetechnisch, aber diesbezüglich ergeben sich in #7 bzw. #9 genannte Probleme/Schönheitsfehler.

Es wäre mir auch wichtig zu verstehen, warum genau der Ansatz von Meniskusschaden zumindest die "Anklickungenauigkeit" eliminiert trotz der verzerrten Höhe der letzten Reihe/Spalte.
 

Thallius

Top Contributor
Grundsätzlich solltest du immer erst multiplizieren und dann dividieren. Damit elimierst du schonmal die Probleme das sich die Zellen verschieben.

Also

Code:
g2.fillRect((size.width * i) COUNT , (size.height * i)/ COUNT , size.width/ COUNT, size.height/ COUNT);

Weiterhin würde ich nur mit geraden Pixeln arbeiten und start dessen einen Rand rechts und links lassen. Also

Code:
int cellWidth = size.width / COUNT;
int cellHeight = size.height / COUNT;
int leftOffset = size.width - (cellWidth * COUNT);
int topOffset = size.height - (cellHeight * COUNT);
g2.fillRect( leftOffset + (cellWidth * i) , cellHeight * i, cell Width, cellHeight);

Dürfte grafisch auch deutlich schöner aussehen.

Gruß

Claus
 

Wolfone

Mitglied
Hallo Thallius, danke für deine Antwort.

Ich bin allerdings auf einige Probleme gestoßen, bei denen ich zugeben muss, dass ich sie noch nicht verstehe.
Im folgenden der Versuch möglichst präzise zu umreißen, was ich gemacht habe.

Zunächst habe ich mal nur deinen Ratschlag bzgl der Multiplikation umgesetzt, was allerdings zu einer Verschiebung der auszufüllenden Rechtecke gegen das Gitternetz geführt hat.

Danach habe ich versucht deine Version zwei zu verbauen und habe auch das Gitternetz mit dem Offset angepasst. Im Code schaut das nun so aus:

Java:
protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Dimension size = this.getSize();
        int cellWidth = size.width/COUNT;
        int cellHeight = size.height/COUNT;
        int leftOffset = size.width - (cellWidth * COUNT);
        int topOffset = size.height - (cellHeight * COUNT);
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.BLUE);
        for (int i = 0; i < COUNT; i++) {
            for (int j = 0; j < COUNT; j++) {
                if (tiles[i][j]) {
                    g2.fillRect( leftOffset + (cellWidth * i) , cellHeight * j, cellWidth, cellHeight);
                    //g2.fillRect(cellWidth * i, cellHeight * j, cellWidth, cellHeight);
                    //g2.fillRect((size.width * i)/ COUNT , (size.height * j)/ COUNT , size.width/ COUNT, size.height/ COUNT);
                }
            }
        }
        g2.setColor(Color.BLACK);
        for (int i = 1; i < COUNT; i++) {
            g2.drawLine(leftOffset+size.width / COUNT * i, 0, leftOffset+size.width / COUNT * i,    size.height);
            g2.drawLine(0, topOffset+size.height / COUNT * i, size.width, topOffset+size.height / COUNT * i);
       }
    }

Allerdings ergeben sich auch dort bei entsprechender Vergrösserung meines Fensters unerwünschte Effekte (siehe Screenshot). Desweiteren ist der Optische Effekt durch den Offset hauptsächlich, dass diese unnatürliche Zellenverlängerung jetzt oben und links anstatt unten und rechts stattfindet.ClickpanelScreenshot.jpg

Ich gebe zu, ich bin verwirrt; kannst du da vielleicht Licht ins Dunkel bringen, was ich falsch gemacht habe?
 

Wolfone

Mitglied
Achjeh, ich habe den Offset in der Höhe vergessen. Nachdem ich jetzt auch die Klickerkennung angepasst habe, habe ich inzwischen einiges mehr an Verständnis bekommen.

Dafür danke ich euch schonmal.

Jetzt hätte ich allerdings noch ein paar etwas technischere Fragen zu der Sache.

Dazu möchte ich den Post von Harry Kane aufgreifen.

Schön wäre es ausserdem, wenn der Post bei der Lösung des Problems helfen würde. Ich sehe nicht, wie der TO durch Ersatz der GridLayout durch ein GridbagLayout und Verwendung der addComponent-Methode irgendwas gewinnt. Der TO hat ja offensichtlich vor allem ein Performanceproblem.
Grundsätzlich rate ich davon ab, grafische Ausgaben mit vielen Einzelelementen, die eigentlich keine Gui sind, über einzelne JComponents zu realisieren. Stattdessen sollte man eine einzelnen JComponent verwenden und das grafische Gedöns in einem Rutsch in einer entsprechenden JComponent zeichnen.
Hier habe ich mal was zusammengeklöppelt:
Java:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingawt;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClickBoard {

    private ClickPanel cp;

    public static void main(String[] args) {
        new ClickBoard("Click Panel");
    }

    public ClickBoard(String titel) {
        JFrame frame = new JFrame(titel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        cp = new ClickPanel();
        frame.getContentPane().add(cp);
        frame.pack();
        frame.setVisible(true);
    }

}

class ClickPanel extends JComponent {

    final int COUNT = 10;

    private boolean[][] tiles = new boolean[COUNT][COUNT];

    public ClickPanel() {
        super();
        addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                int x = e.getX();
                int y = e.getY();
                flip(x, y);
          }

          });
      setPreferredSize(new Dimension(300, 300));
    }

    public void flip(int x, int y) {
        Dimension size = getSize();
        int i = Math.min(x * COUNT / size.width, COUNT - 1);
        int j = Math.min(y * COUNT / size.height, COUNT - 1);
        tiles[i][j] = !tiles[i][j];
        repaint();

    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        Dimension size = getSize();
        g2.setColor(Color.GREEN);
        for (int i = 0; i < COUNT; i++) {
            for (int j = 0; j < COUNT; j++) {
                if (tiles[i][j]) {
                    g2.fillRect(size.width / COUNT * i, size.height / COUNT * j, size.width / COUNT, size.height / COUNT);
                }
            }
        }
        g2.setColor(Color.BLACK);
        for (int i = 1; i < COUNT; i++) {
            g2.drawLine(size.width / COUNT * i, 0, size.width / COUNT * i,    size.height);
            g2.drawLine(0, size.height / COUNT * i, size.width, size.height / COUNT * i);
       }
    }

}

Ich habe bis jetzt nämlich nur Vermutungen, was da intern passiert (insbesondere bei repaint()) und aus der Online-Documentation für Java 8 bin ich nicht so richtig viel schlauer geworden.

Ich lege mal meine Auffassung des Ganzen dar, wäre nett, wenn jemand das bestätigen oder verbessern könnte.


1. paintComponent() ist eine Methode, die jede Klasse, die von JComponent erbt nativ mitbringt.
2. wir überschreiben diese mit unserer eigenen Version in der wir zunächst die "Standardvariante"
(super.paintC...) aufrufen, was uns unseren Basisuntergrund schonmal zeichnet. Also den grauen Hintergrund.
3. wir casten auf Graphics2D um auf die Zeichenmethoden zugreifen zu können und setzen die Farbe in der zu zeichnen sein soll auf Blau.
4. Alle "tiles, die true sind" werden blau gezeichnet.
5.Wir zeichnen das Grid erst als letztes, da das uns erspart dafür sorgen zu müssen, dass das Blau der Rechtecke unter Umständen das Grid "übermalt".
(Soweit in Ordnung?)

6. Wird ein Flip aufgerufen wird schlussendlich ein repaint() aufgerufen. Ich vermute mal, dass das beinhaltet, dass paintComponent(..) aufgerufen wird,richtig?

Weiterhin vermute ich, dass paintComponent(..) ebenfalls automatisch aufgerufen wird, wenn ich das Fenster vergrößere, richtig?
Daraus resultiert eine letzte Frage: wie würde ich es anstellen, wenn das Clickpanel nicht mit dem Frame wachsen soll?


Ich weiß...viel, aber der Thread ist wirklch lehrreich für mich, deswegen wäre ich erfreut über weitere Erleuchtung :)
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
H Viele ActionListener (MouseListener) - Performance AWT, Swing, JavaFX & SWT 24
missy72 JavaFX Performance / ImageView in TableView über TimeLine AWT, Swing, JavaFX & SWT 1
L JavaFX ListCell Performance AWT, Swing, JavaFX & SWT 10
E Swing Miserable Performance beim Ändern der Hintergrundfarbe von JLabels AWT, Swing, JavaFX & SWT 3
C Pixel-Rendering/Animation Performance in BufferedImage AWT, Swing, JavaFX & SWT 1
I JavaFX Graphics Performance AWT, Swing, JavaFX & SWT 2
S Swing Performance bei Verschachtelung problematisch AWT, Swing, JavaFX & SWT 0
R Performance Drag and Drop & Timer AWT, Swing, JavaFX & SWT 3
O AWT Performance und Bug behebung[brauche Hilfe] AWT, Swing, JavaFX & SWT 2
T Swing Allgemeines Problem mit der Performance bei DragAndDrop AWT, Swing, JavaFX & SWT 2
T Prüfen ob 2 JLabel übereinander liegen. Performance Problem. AWT, Swing, JavaFX & SWT 5
S Swing Lauftext Performance Probleme, in größerer Anwendung AWT, Swing, JavaFX & SWT 6
B Performance-Probleme AWT, Swing, JavaFX & SWT 17
D DefaultTableCellRenderer - Performance AWT, Swing, JavaFX & SWT 3
hdi Swing [Umfrage] Swing Performance AWT, Swing, JavaFX & SWT 27
B 2D-Grafik BufferedImage Performance AWT, Swing, JavaFX & SWT 3
C Performance-Problem beim Überschreiben von paintComponent() AWT, Swing, JavaFX & SWT 2
Hausmeister JTable mit Bildern - Performance AWT, Swing, JavaFX & SWT 5
J JTree Performance AWT, Swing, JavaFX & SWT 2
Developer_X Swing Graphics2D translate zerstört performance AWT, Swing, JavaFX & SWT 2
hdi Swing JTable: Mein CellRenderer ist ein Performance-Killer? AWT, Swing, JavaFX & SWT 7
J Performance bei mouseMoved(...) AWT, Swing, JavaFX & SWT 4
L JFreeChart - Performance bei PNG-Erstellung AWT, Swing, JavaFX & SWT 5
P seltsame Performance Probleme bei 2 Guis abhängig vom Aufruf AWT, Swing, JavaFX & SWT 8
G Performance beim Zeichnen erhöhen? AWT, Swing, JavaFX & SWT 21
hdi bitte um performance ratschläge AWT, Swing, JavaFX & SWT 31
G performance fragen zu AWT, Swing AWT, Swing, JavaFX & SWT 14
T (Java 6) Thumbnails in JFileChooser - Performance steigern? AWT, Swing, JavaFX & SWT 3
hdi schlechte performance bei simplem swing AWT, Swing, JavaFX & SWT 9
G Probleme mit Performance bei einer Tabelle AWT, Swing, JavaFX & SWT 16
M Performance SWT ??? AWT, Swing, JavaFX & SWT 8
D performance problem: paintcomponent, alphacomp, bufferedImag AWT, Swing, JavaFX & SWT 10
P SWT: StyledText Performance steigern? AWT, Swing, JavaFX & SWT 2
T Performance Problem bei BufferedImage AWT, Swing, JavaFX & SWT 3
P SWT Performance : "Text" - Ausgabe beschleunigen ? AWT, Swing, JavaFX & SWT 21
O performance g2d.drawImage() AWT, Swing, JavaFX & SWT 17
D Performance Probleme Jtable AWT, Swing, JavaFX & SWT 4
N Performance (BufferStrategy?) AWT, Swing, JavaFX & SWT 2
F Problem mit Transparenz, MouseEvents und Performance AWT, Swing, JavaFX & SWT 3
O LookAndFeel und Performance AWT, Swing, JavaFX & SWT 7
W Performance verbessern AWT, Swing, JavaFX & SWT 2
S TableCellRenderer, Performance AWT, Swing, JavaFX & SWT 9
S Performance-Problem: JTextArea als Logging-Window AWT, Swing, JavaFX & SWT 8
B Was würden Profis an meiner Klasse verbessern? AWT, Swing, JavaFX & SWT 1
S Bildqualität verbessern AWT, Swing, JavaFX & SWT 3
F Wie Oberfläche verbessern? (JButtons, Textfeld verkleinern) AWT, Swing, JavaFX & SWT 5
B Code Architektur verbessern. AWT, Swing, JavaFX & SWT 4
I Windows LaF verbessern? AWT, Swing, JavaFX & SWT 12

Ähnliche Java Themen

Neue Themen


Oben