[Java] Fenster zeichnen klappt nicht wie gewollt.

Roin

Freier Denker
Registriert
22 Juli 2013
Beiträge
581
Hallo Leute,

ich habe ein kleines Programm geschrieben. Die Logik in dem Programm funktioniert ohne Probleme, allerdings gibt es Probleme beim Fenster zeichnen, die ich nicht nachvollziehen kann.
Es handelt sich hierbei um eine selbsterstellte Version von Canvays Game of Life.
Das Problem ist, dass das Spielfeld, in dem die Zellen angezeigt werden, sich bei jedem neuzeichnen leicht verschiebt und somit meistens die ganz rechten und die unteren Zellen gar nicht oder nur teilweise dargestellt werden, weil der Rest verdeckt wird.
ConvaysGameOfLife Screenshot defekt.jpg

Vielleicht kann mir ja jemand sagen, welche Codezeile da den Mist baut.
[src=java]
//Standardwerte
private int fieldsize = 10;
private String startField = "";
private int fieldDimensionMin = 1;

private final int topPanelHeight = 26;
private final int bottomPanelHeight = 36;
private final int fieldBorderWidth = 5;

//Allgemeine Variableninitialisierung
private Field field;
private JFrame w;
private JPanel top;
private JPanel bottom;
private final JLabel generationNumber = new JLabel("0");
private final JPanel fieldPanel = new JPanel();
private final Color colorAlive = Color.RED;

/**
* Erstellt das Fenster, in welchem das Spiel dargestellt wird.
*/
private void buildWindow() {
//Ein paar Höhen und Breiten für das Fenster definieren / berechnen.
// int topPanelHeight = 26;
// int bottomPanelHeight = 36;
// int fieldBorderWidth = 5;
int width = this.fieldsize*(this.fieldDimensionMin+1) +1 + fieldBorderWidth*2;
if(width < 350) {
width = 350;
}
int height = this.fieldsize*(this.fieldDimensionMin+1) +1 + topPanelHeight + bottomPanelHeight + fieldBorderWidth*2;
if(height < 350 + topPanelHeight + bottomPanelHeight) {
height = 350 + topPanelHeight + bottomPanelHeight;
}

//Fenster erstellen und Grundparameter einstellen
JFrame window = new JFrame();
this.w = window;
window.setSize(width, height);
window.setPreferredSize(new Dimension(width, height));
window.setLocationRelativeTo(null);
window.setTitle("Convays Game of Life");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Container für das komplette Fenster erstellen.
JPanel all = new JPanel();
all.setSize(window.getWidth(), window.getHeight());
all.setPreferredSize(new Dimension(window.getWidth(), window.getHeight()));
all.setLayout(new BoxLayout(all, BoxLayout.PAGE_AXIS));

// System.out.println("---------");
// System.out.println(this.w.getHeight());
// System.out.println(this.w.getWidth());
// System.out.println(all.getHeight());
// System.out.println(all.getWidth());
//
// this.w.pack();
//
// System.out.println("---------");
// System.out.println(this.w.getHeight());
// System.out.println(this.w.getWidth());
// System.out.println(all.getHeight());
// System.out.println(all.getWidth());


//Container für die Überschrift und die Generationsanzeige
this.top = new JPanel();
this.top.setSize(all.getWidth(), topPanelHeight);
this.top.add(new JLabel("Generation "));
this.top.add(this.generationNumber);
// top.setBackground(Color.RED);

all.add(this.top, BorderLayout.PAGE_START);

//Spielfeld-Container definieren
// this.fieldPanel.setSize(all.getWidth()-fieldBorderWidth*2, all.getHeight() - topPanelHeight - bottomPanelHeight-fieldBorderWidth*2);
// this.fieldPanel.setPreferredSize(new Dimension(all.getWidth()-fieldBorderWidth*2, all.getHeight() - topPanelHeight - bottomPanelHeight-fieldBorderWidth*2));
this.fieldPanel.setSize(width-fieldBorderWidth*2, height - topPanelHeight - bottomPanelHeight-fieldBorderWidth*2);
this.fieldPanel.setPreferredSize(new Dimension(width-fieldBorderWidth*2, height - topPanelHeight - bottomPanelHeight-fieldBorderWidth*2));
// this.fieldPanel.setMinimumSize(new Dimension(width-fieldBorderWidth*2, height - topPanelHeight - bottomPanelHeight-fieldBorderWidth*2));
// this.fieldPanel.setBackground(Color.BLUE);

all.add(this.fieldPanel);

//Container für die Buttons erstellen
this.bottom = new JPanel();
this.bottom.setSize(all.getWidth(), bottomPanelHeight);

//Next Gen-Button
JButton b_next = new JButton("Nächste Generation");
b_next.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
nextGeneration();
}
});
b_next.setVisible(true);
this.bottom.add(b_next);

//Neustart-Button
JButton b_restart = new JButton("Neustart");
b_restart.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
restart();
}
});
b_restart.setVisible(true);
this.bottom.add(b_restart);

//Beenden-Button hinzufügen
JButton b_close = new JButton("Beenden");
b_close.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
b_close.setVisible(true);
this.bottom.add(b_close);

// bottom.setBackground(Color.ORANGE);
all.add(this.bottom, BorderLayout.PAGE_END);

//Fenster packen
window.getContentPane().add(all);
window.pack();

//StartZellen platzieren
this.nextGeneration();

//Fenster anzeigen lassen
window.setVisible(true);

// System.out.println("---------");
// System.out.println(this.w.getHeight());
// System.out.println(this.w.getWidth());
// System.out.println(all.getHeight());
// System.out.println(all.getWidth());
}

/**
* Zeichnet das Spielfeld neu, um den aktuellen Spielstand anzuzeigen.
*/
private void repaint() {
//Alle Pixel in einer Grafik zeichnen lassen
final JPanel fPanel = this.fieldPanel;
final ArrayList<Cell> cells = this.field.getCells();
final Color cAlive = this.colorAlive;

// this.bottom.setSize(this.bottom.getWidth(), this.bottomPanelHeight);
// this.bottom.setPreferredSize(new Dimension(this.bottom.getWidth(), this.bottomPanelHeight));
// this.top.setSize(this.top.getWidth(), this.topPanelHeight);
// this.top.setPreferredSize(new Dimension(this.top.getWidth(), this.topPanelHeight));

//Position des Spielfeldes in X-Richtung bestimmen
int oX = this.w.getWidth()-this.fieldsize*(this.fieldDimensionMin+1) +1;
oX = oX/2;
if(oX < 0) {
oX = 0;
}
final int offsetX = oX;

//Position des Spielfeldes in Y-Richtung bestimmen
int oY = this.w.getHeight()-this.fieldsize*(this.fieldDimensionMin+1) +1 - this.bottom.getHeight() - this.top.getHeight();
oY = oY/2;
if(oY < 0) {
oY = 0;
}
final int offsetY = oY;

// System.out.println(this.w.getHeight());
// System.out.println(this.w.getWidth());
// System.out.println(this.top.getHeight());
// System.out.println(this.top.getWidth());
// System.out.println(this.bottom.getHeight());
// System.out.println(this.bottom.getWidth());
// System.out.println(oX);
// System.out.println(oY);
// System.out.println(fPanel.getWidth()-offsetX*2);
// System.out.println(fPanel.getHeight()-offsetY*2);

//Komponente definieren, die gezeichnet wird
JComponent tmp = new JComponent() {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.clearRect(offsetX, offsetY, fPanel.getWidth()-offsetX*2, fPanel.getHeight()-offsetY*2);
//g.clearRect(offsetX, offsetY, fPanel.getWidth(), fPanel.getHeight());
for (Cell c : cells) {
//Zellrand zeichnen
g.setColor(Color.BLACK);
g.fillRect((c.x-1) * (fieldDimensionMin+1) + offsetX,
(c.y-1) * (fieldDimensionMin+1) + offsetY,
fieldDimensionMin+2, fieldDimensionMin+2);
//Zelle zeichnen
if(c.isAlive()) {
g.setColor(cAlive);
g.fillRect((c.x-1) * (fieldDimensionMin+1)+1 + offsetX,
(c.y-1) * (fieldDimensionMin+1)+1 + offsetY,
fieldDimensionMin, fieldDimensionMin);
} else {
g.setColor(Color.WHITE);
g.fillRect((c.x-1) * (fieldDimensionMin+1)+1 + offsetX,
(c.y-1) * (fieldDimensionMin+1)+1 + offsetY,
fieldDimensionMin, fieldDimensionMin);
}
}
}
};
tmp.setPreferredSize(new Dimension(fPanel.getWidth(), fPanel.getHeight()));
// tmp.setBackground(Color.GREEN);

//Neues Spielfeld übernehmen
if (fPanel.getComponentCount() != 0) {
fPanel.remove(fPanel.getComponent(0));
}
fPanel.add(tmp);

//Fenster neu zeichnen lassen.
this.w.revalidate();
}[/src]

Falls das nicht reicht, hier die kompletten Dateien: Anhang anzeigen 0.9.rar
 
Zuletzt bearbeitet:
Ich kann dein Problem mit der Jar nicht reproduzieren - in Generation 500 war das Feld weiterhin da, wo es sein sollte. Einige Teile sind überdeckt, das waren sie aber von Anfang an. Soweit sehe ich in deinem Code auch keine Probleme. Kannst du eventuell ein Video von dem Problem machen?
 
  • Thread Starter Thread Starter
  • #3
Ich habe es ein bisschen falsch dargestellt. Ich meinte damit, dass manche Teile am Anfang überdeckt sind. Dann ziehe ich manuell das Fenster auf die passende Größe und bei der nächsten Generation wandert das ganze Spielfeld runter und ist mindestens unten wieder überdeckt. Irgendwie wird also die Größe von dem "top"-Container immer Größer, als ich es wünsche. Ich hätte ihn gerne bei ca. 30px fixiert. Allerdings wird die Höhe immer größer, egal wie ich alle anderen Container verändere...
 
Ah, okay, jetzt kann ich dein Problem auch reproduzieren. Das ganze scheint nach einer kurzen Recherche vom BoxLayout zu kommen, das du nimmst - wenn du ein anderes Layout, beispielsweise GridLayout nimmst, solltest du keine Probleme mehr haben. Alternativ könntest du die Positionierung des Felds verändern - es richtet sich momentan immer nach der absoluten Fensterhöhe, wenn du stattdessen nur den nicht verdeckten Teil nimmst, hast du auch keine Probleme mehr.
 
  • Thread Starter Thread Starter
  • #5
Danke für deinen Vorschlag. Ich werde es gleich mal mit dem anderen Layout ausprobieren. Das mit der Positionierung habe ich bereits gesucht, allerdings nicht gefunden. wie heißen die Funktionen dafür?

Das GridLayout positioniert mit new GridLayout(0, 1) die Container zwar untereinander, allerdings sind die Größen da noch alle gleich (beziehungsweise gleich hoch) - da muss ich noch gucken, wie ich das verändern kann.

Ich habe zwar über Google gefunden, dass es sowas wie setRowExpandRatio() geben soll, aber die Funktion gibt es irgendwie nicht in der Klasse von dem GridLayout, welches ich importiert habe (und ich habe das aktuelle JDK).

--------
Ich habe das Problem durch einen Geistesblitz lösen können.
In der repaint-Methode hatte ich einen Y-Offset berechnen lassen. Durch diesen ist dann alles kaputt gegangen, da dieser nicht nur die Höhe des Spielfeldes, sondern des ganzen Fensters berücksichtigt hat.
 
Zuletzt bearbeitet:
  • Thread Starter Thread Starter
  • #7
Der Hinweis auf den Wettbewerb kam von mir :)
Und Hilfe ist erlaubt ;-)
Im Endeffekt habe ich den Fehler aber trotzdem selber gefunden - nur die Eingebung kommt meistens erst nach dem Fragen :-D
 
Zurück
Oben