Panel grösse vor setVisible auftreiben

Hi Leute.
Ich dachte ich schreib mal um micht ein bisschen abzulenken ein kleines fensterchen mit malfunktion, eingeschätzte zeit: max eine stunde, nix da.
Ich komm einfach nicht klar mit díesen layout “managern”. Also folgendes Problem:
Hab ein kleines Standard layout gebastelt, borderlayout, north menu, east tools, west layers, south info, und center - darum gehts - die drawPane.
Diese DrawPane nimmt ja bekanntlich den größtmöglichsten platz ein. Funktionieren tut das ganze so, dass ich ein bild da drin hab, wo drauf auch mit getGraphics gezeichnet wird, dieses bild wird dann eben in der paintComponenet gezeichnet. Nun ja, dieses bild sollte ja am besten die selbe größe haben wie das panel. naja, new BufferedImage(getWidth(), getHeight(), …) lieferte mir ein bild mit der größe -34 -64 (What The FUCK?). Achso ja, dacht ich mir, die größen die der manager erstellt sind ja erst nach setVisible() verfügbar. nun gut, hab den listener so umprogrammiert das er nur was macht wenn die variable “loaded” true ist, und die ist es nur wenn init() aufgerufen wird, und das wird erst nach setVisible() aufgerufen. yeah, programm gestartet: bild grösse 10 10. Super. was läuft da falsch? Eine einzige möglichkeit hat geklpappt: In der init mit thread.sleep 2 sekunden warten, und dann loaded true setzen und fortfahren. Ist ja eine seehr saubere variante. aber warum ist das so? ich meine, ich dachte in setVisible() werden die größen auch richtig gesetzt und so weiter? wie kann es sein das die eigentlichen werte erst lange (also… “lange”) nach setVisible() richtig bzw verfügbar sind? Und wie soll ich das am besten implementieren? .pack() hats auch nicht gebracht, und eigentlich will ich mir die unnötige rechenleistung if(loaded)… auch ersparen. (ich weiss, das macht keinen / fast keinen unterschied aber es ist einfach nicht schön finde ich.) also gibt es denn gar keine möglichkeit an die grösse zu kommen bevor setVisible() aufgerufen und eine viertel ewigkeit vergangen ist?

VIelen Dank!

*** Edit ***

Validate / revalidate bringts auch nicht, dann ist die grösse auf einmal 0 0

wird setVisible im EDT aufgerufen? Die Größe der Komponente ist erst verfùgbar, wenn diese angezeigt wird != Aufruf von setVisible

  1. Was ist edt?
  2. Ja genau das ist das problem. Das teil wird nauerlich sichtbar gemacht, ich sehs ja direkt nach dem dtart. Aber die richtige groesse bekomm ich trozdem nicht DIREKT nach dem anzeigen…

Kurze Antwort: Find’ dich damit ab :stuck_out_tongue:

Lange Antwort: Wenn man setVisible aufruft, wird im Hintergrund die gesamte Maschinerie in Gang gesetzt, die dafür sorgt, dass „das alles so leicht ist“. Wann genau in diesem Prozess der LayoutManager welche Größe bestimmt, ist schwer zu sagen, aber eigentlich sollte das auch nicht wichtig sein. Es müssen praktisch immer Kompromisse eingegangen werden. Deswegen haben Components nur eine „preferred size“. Das ist die Größe, die Components von sich aus gerne hätten, aber ob sie diese Größe bekommen, entscheidet der LayoutManager salomonisch und unter Abwägung aller Argumente. Die Standard-PreferredSize von einem JPanel ist AFAIK 10x10, aber das ist nicht, worauf man sich verlassen sollte. Das mit dem „loaded“-Flag ist u.U. gar nicht so falsch (da noch mit irgendwelchen Threads oder sleep()s rumzupfuschen aber schon). Wenn man z.B. ein BufferedImage haben will, das immer so groß ist, wie das JPanel, macht man eben sowas wie

private BufferedImage image;

@Override 
protected void paintComponent(Graphics g)
{
    super.paintComponent(g);
    if (image == null || image.getWidth() != getWidth() || image.getHeight() != getHeight())
    {
        image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
    }
    ...

EDT: The Event Dispatch Thread

ComponentListener.componentResized(ComponentEvent) sollte funktionieren.

Folgende Konstruktion funktioniert bei mir auch, aber ich empfehle sie ausdrücklich NICHT, unter anderem weil

[ul]
[li]das setSize(int, int) nach pack() hässlich ist[/li][li]ein Kollege beim Refactoring das pack() ohne Test entfernen wird[/li][li]ich nicht darüber nachdenken will, ob das Funktionieren garantiert ist[/li][/ul]

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class SizeTest {

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {

                JPanel panel = new JPanel();
                panel.setBackground(Color.ORANGE);
                
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(panel);

                frame.pack();
                frame.setSize(500, 400);
                frame.revalidate();
                
                System.out.println("vor setVisible(true): " + panel.getSize());
                
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                System.out.println("nach setVisible(true): " + panel.getSize());
            }
        });
    }
}```
Man kann auch Varianten ohne pack() bauen und sich dann streiten, was am wenigsten unschön ist.

NIEMALS new BufferedImage in der Paint Methode machen, am besten gar kein new da drin! Das ist der Horror für die Performance. Eine Bildkomponente ist wirklich nicht die Welt:

class ImageComponent extends JComponent
{
Image image;

ImageComponent(Image image)
{
this.image=image;
}

public void paintComponent(Graphics g)
{
if(image!=null)
{
g.drawImage(image,0,0,g.getClipBounds().getWidth(),g.getClipBounds().getHeight());
}
}

Hmmm das problem ist nur… obwohl…
Kann ich dann auch mit gevraphics arbeiten? Ich dachte eigentlich das bei repaint () erst alles geloscht wird, aber… mal sehn.
Zum loaded flag: das funktioniert eben nur, wenn ich nach dem starten ein bisschen warte.
Ch glaube … mir faellt grad ein kleiner hack mit onresize ein, ich versuchs mal
Danke euch

*** Edit ***

Ich habs jetzt zur hälfte gelöst:
Ich mach es jetzt zwar immer noch nach setVisible() aber nicht direkt danach sondern mit SwingUtilites.invokeLater. so klappt es ganz gut. Danke euch nochmal :wink:

Das kann ich so nicht stehen lassen. In der angedeuteten Lösung wird das neue Bild nur erzeugt, wenn (es noch nicht existiert oder) sich die Größe geändert hat UND es gebraucht wird - also genau dann, wenn es nötig ist. (Und…wo sollte denn in deinem Beispiel immer das Bild mit der passenden Größe herkommen?)

Das Bild wird immer so groß wie die Komponente - genauer die Clip Bounds der Graphics - ist. Angenommen du ziehst das Fenster auseinander. Jetzt wird sehr oft hintereinander die paintComponent aufgerufen und dein Bild neu erstellt. Das ergibt dann meistens unschönes Flackern.

Dann wuerde ich persoenlich einfach einen flag repainting oder so machen und bei onresize false stellen

Habs jetzt nur fix überflogen, aber ich gebe bERt0r recht. Rechenintensive Aufgaben gehören nicht in die paint(Component).
Sorge dafür, das ein entsprechendes Imageobjekt zur verfügung steht und nutze das Graphicsobjekt um es zu zeichen und zwar so, das es in die darstellende Komponente passt.
Und nicht irgendwelche Flags um onResize zu blocken und vor oder nach setVisible irgendwelche mauscheligen Framesizes zu generieren.
Es ist sicher günstiger und auch klüger auf Grund diverser Umgebungsvariablen (Usereinstellungen oder Screensize etc.) eine angemessene Initialgröße für deinen JFrame festzulegen.
Ich denke mal in etwa das meinte Marco mit : „Finde dich damit ab!“ :stuck_out_tongue_winking_eye:

Gruß Vanny

Nein, damit meinte ich nur, dass man eben die Größe nicht bekommt, bis “alles fertig” ist. Und “alles fertig” ist spätestens oder frühestens (?) dann, wenn paintComponent aufgerufen wird. Und natürlich gehören “rechenintensive” Aufgaben nicht in die paintComponent. Aber erstens ist das Erstellen eines Bildes nicht “rechenintensiv” in diesem Sinn, und zweitens wird das ja (nochmal: ) NUR gemacht, wenn es notwendig ist. Immer, wenn gezeichnet wird, soll ein Bild mit der passenden Größe existieren. Wie sollte man das erreichen, OHNE das in paintComponent zu machen? Einen ComponentListener ranhängen und bei componentResized das Bild aktualisieren wäre in diesem Fall IMHO “von hinten durch die Brust ins Auge”, und hätte … genau den gleichen Effekt (!?!)

Ich schmeiss mich weg XD

Von hinten durch die Brust ins Auge halte ich eher, jedes mal wenn man die Fenstergröße verändert auch die Bildgröße in seinem Malprogramm zu verändern. Angenommen du zeichnest was, dann schiebst du dein Fenster zusammen und plötzlich ist nur mehr die halbe Zeichnung da… Die größe der DrawPane sollte egal sein, am besten sollte die gleich in ein Scrollpane gepackt werden. In jedem vernünftigen Bildbearbeitungsprogramm kann man die Größe seiner Zeichnung fix einstellen, unabhängig davon wie groß das Fenster ist.

Nun, es ist nicht direkt ersichtlich, dass es um ein “Malprogramm” geht, aber in Anbetracht der anderen Threads könnte das sogar sein. Natürlich würde das da keinen Sinn machen. Ich bin eher von einem Fall ausgegangen, wie man ihn bei AWT mit manuellem Double-Buffering oft hatte ( http://wiki.byte-welt.net/wiki/DoubleBuffering_im_AWT_-_Flackern_verhindern ). Vermutlich mal wieder ein Fall, wo schlicht die Frage bzw. die Absichten nicht klar genug waren.