AWT Programm funktioniert nicht richtig

Pianoman.20

Mitglied
Hallo,

Kann mir jemand sagen weshalb der im rot markierte Code nur einmal funktioniert?

Ich habe versucht durch den ComponentAdapter die Grösse des JFrames abzufragen und diese mit dem Bild (hintergrund) anzugleichen. Nur tut es das einmal und danach entsteht irgendwie ein Bug.
 

Anhänge

  • Unbenannt.PNG
    Unbenannt.PNG
    62,6 KB · Aufrufe: 0

Marinek

Bekanntes Mitglied
Möglicherweise fehlt ein Repaint an der Stelle.

Ich habe schon ewig und drei Tage kein Swing implementiert.

Läuft der Listener auch im EDT??
 

KonradN

Super-Moderator
Mitarbeiter
Das sind doch normale Reaktionen…. Der Hinweis auf das repaint ist doch schon gegeben worden, daher ist der Mehrwert Deines Posts halt nicht wirklich gegeben.

Und das revalidate macht bei dem gezeigten Code keinen Sinn …
 

KonradN

Super-Moderator
Mitarbeiter
muss ich dich wirklich belehren? man sollte beides aufrufen. die antwort von @Apple’s Jünger war also sogar möglicherweise schädlich.
Wenn Du Argumente für diese Aussage hast, dann würde ich die gerne hören. Vielleicht kann ich ja doch einmal etwas von Dir lernen.

Hier halte ich es aber für extrem unwahrscheinlich. Denn man muss einfach nur verstehen, was die einzelnen Methoden machen - dann kann man auch klar sagen, was man braucht und was man nicht braucht.

repaint:
Component (Java SE 17 & JDK 17) (oracle.com)
Repaints this component.
If this component is a lightweight component, this method causes a call to this component's paint method as soon as possible. Otherwise, this method causes a call to this component's update method as soon as possible.

Note: For more information on the paint mechanisms utilitized by AWT and Swing, including information on how to write the most efficient painting code, see Painting in AWT and Swing.

revalidate:
Component (Java SE 17 & JDK 17) (oracle.com)
Revalidates the component hierarchy up to the nearest validate root.
This method first invalidates the component hierarchy starting from this component up to the nearest validate root. Afterwards, the component hierarchy is validated starting from the nearest validate root.

This is a convenience method supposed to help application developers avoid looking for validate roots manually. Basically, it's equivalent to first calling the invalidate() method on this component, and then calling the validate() method on the nearest validate root.

Wie als klar erkennbar ist: Ersteres triggert lediglich ein erneutes Malen der Componente (was hier fehlen kann).
Letzteres ist nur sinnvoll / notwendig, wenn an der Hirarchie etwas geändert wurde. Aber das ist ja ganz offensichtlich nicht der Fall.

Wenn man aber von den Abläufen keine Ahnung hat, dann mag man evtl. der Meinung sein: "Ich rufe einfach beides auf". Und das vorsichtshalber an ganz vielen Stellen im Code. Das macht aber natürlich keinen Sinn. Und als Regel macht es auch nur Sinn, wenn man Jemanden nicht mit Details belasten will. Ein Lehrer, der einem Schüler sowas sagt wird der Meinung sein, dass die Hintergründe zu schwer zu verstehen sind oder er hat sie selbst nicht verstanden....

Wie dem auch sei - ich selbst distanziere mich davon, denn aus meiner Sicht ist eine halbwegs professionelle Softwareentwicklung das Ziel für die Leute, die hier Fragen stellen. Und dazu gehört dann immer ein zumindest grobes Verständnis der Abläufe.
 

Marinek

Bekanntes Mitglied
HINT: Lesen bildet. Und deine Postings, sind zwar für deine Verhältnisse halt sprachlich "OK" aber mind. redundant (siehe Ausführung von @KonradN weiter oben) oder einfach falsch.

Also lies mal den StackOverflow und bildet dich einfach weiter, statt hier sinnlose Postings einfach abzuschreiben und dann für deine zu verkaufen.

und btw: man würde das so, wie im screen gezeigt, überhaupt nicht machen
Hier wäre natürlich jetzt ein Beispiel gut, wie man es denn machen würde. Vielleicht auch etwas ohne hundert static Methoden? - Bevor man hier sowas droppt und sich maximal unbeliebt macht, vor allem nachdem man direkt darauf hingewiesen wird...

Es tut mir einfach so leid, dass hier eine einfache Frage eines TO in so einer Schlammschlacht enden muss, nur weil Tobias hier seine Tage hat...

eigentlich überschreibt man die paint-Methode der Komponente, aber egal, vielleicht ist das schon zu professionell, das wollen wir ja nicht
Ja klar kann man die paint() überschreiben.. Aber das hat ja ein ZWECK. Und den wirst du lieber Tobias niemals verstehen mangels Erfahrung.

@Pianoman.20 ; Ich kann mich hier nur für den Troll User entschuldigen. Ich sehe die Schuld bei den "bekannten" Mitgliedern, die genau wissen wer das ist, aber dennoch immer und immer wieder auf die Schnauze fallen, weil man immer und immer wieder eine neue faire Chance geben und das wird mit Füßen getreten.

Ich kann dir anbieten, wenn du Fragen hast, schreib mich an, ich erkläre dir die nächsten Fragen im Discord als Wiedergutmachung.
 

KonradN

Super-Moderator
Mitarbeiter
Hast Du den Beitrag auch gelesen? Wenn man an der Hierarchie etwas ändert (Ups, zu allgemein. Wenn man remove oder removeAll aufruft), dann ruft man revalidate und repaint auf.

Nur da hier an der Hierarchie der AWT / Swing Komponenten nichts verändert wurde, ist das hier eben nicht notwendig.

In dem SO Thread steht also auch nichts anderes, als ich schon geschrieben habe, nur eben ging es bei SO um ein konkretes Problem und ich habe es allgemeiner gehalten.

und btw: man würde das so, wie im screen gezeigt, überhaupt nicht machen

eigentlich überschreibt man die paint-Methode der Komponente, aber egal, vielleicht ist das schon zu professionell, das wollen wir ja nicht
Das ist nicht zwingend gesagt. Er nutzt die Möglichkeiten der Klasse ImageIcon. Das ist durchaus eine elegante Möglichkeit, wenn man einfach nur ein Bild anpassen will. Das ist also eine nicht unübliche Vorgehensweise.

Und nein, man überschreibt per se nicht die paint Methode. Das wäre nur bei Top Level Containern wie JFrame korrekt. Ansonsten wäre es paintComponent. Da wir nicht wissen, was der TE genau braucht, sollte man da also entweder genau sein oder eben klare Links aufzeigen wie z.B. zu Painting in AWT and Swing (oracle.com)

Solche pauschalen Antworten, die viele Details einfach weg lassen, sind also absolut nicht professionell - aber Du hast ja selbst gesagt: Das willst Du nicht (Hier als auch in dem anderen Thread beim Thema Clean Code ...) Ist ok, so eine Sicht darfst Du gerne haben. Aber ich finde solche Antworten den Fragestellern gegenüber zumindest unfair.
 

KonradN

Super-Moderator
Mitarbeiter
@Pianoman.20 ; Ich kann mich hier nur für den Troll User entschuldigen. Ich sehe die Schuld bei den "bekannten" Mitgliedern, die genau wissen wer das ist, aber dennoch immer und immer wieder auf die Schnauze fallen, weil man immer und immer wieder eine neue faire Chance geben und das wird mit Füßen getreten.
Ich find es schon gut, wenn man im Forum bleibt. Den Troll kannst Du reporten. Dann wird sich irgendwann etwas tun. Die Moderatoren haben leider teilweise nicht so viel Zeit, wie das Forum manchmal erfordert ...

Aber ich denke, dass die fachlichen Erwiderungen doch durchaus vernünftig sind und auch dem TE weiter helfen könnten. Das Problem ist halt nur, dass ich gewisse Aussagen so nicht stehen lassen möchte und daher mich tatsächlich mehr oder weniger gezwungen sehe, da eine Richtigstellung zu schreiben.
 

KonradN

Super-Moderator
Mitarbeiter
Kannst Du ggf. noch im Detail beschreiben, was nicht geht bzw. was das Verhalten ist, das Du siehst?

Evtl. reicht es nicht, nur das Hintergrundbild zu setzen sondern Du musst ggf. auch die Größe des JLabels anpassen.
 

Marinek

Bekanntes Mitglied
Ja, also nach der Diskussion weiter oben, würde ich eher darauf schließen, dass die Hinweise von dem User eher Müll sind.

Versuch mal Hintergrund.repaint() aufzurufen.

Im übrigen beachte, was Konrad schrieb.
 

Pianoman.20

Mitglied
Kannst Du ggf. noch im Detail beschreiben, was nicht geht bzw. was das Verhalten ist, das Du siehst?

Evtl. reicht es nicht, nur das Hintergrundbild zu setzen sondern Du musst ggf. auch die Größe des JLabels anpassen.
Also wenn ich das Fenster mit der Maus vergrössere, vergrössert es das Bild bei ersten Mal mit. Danach funktioniert es nicht mehr, bzw. wenn ich es z.B. verkleinern möchte, schneidet es das Bild nur zu und verkleinert es nicht. Wenn ich es dann wieder vergrössere, macht es mit dem Bild gar nichts mehr und es bleibt nebst dem Bild eine schwarze Fläche (siehe Anhang).

Das mit dem JLabel werde ich noch versuchen.
 

Anhänge

  • IMG_20230927_173939673_MFNR.jpg
    IMG_20230927_173939673_MFNR.jpg
    281,7 KB · Aufrufe: 0

KonradN

Super-Moderator
Mitarbeiter
Poste mal deinen ganzen Code in Code Tags (Also bitte über den Knopf </> links oberhalb des Eingabebereiches. Dann teste ich da auch mal lokal und zeige eine Lösung, die den Weg nutzt, den Du nehmen willst.

Ich vermute auch, dass Du Qualitätsverluste durch ständiges Größe verändern bekommst - wobei das erst eine Vermutung ist, aber das setImage (getImage(), ...) sieht etwas danach aus.
 

Pianoman.20

Mitglied
Java:
public class GUI {

    public GUI() {

        //Bilder
        ImageIcon hintergrund = new ImageIcon("Pfad");

        //Fenster
        Frame frame = new JFrame("Erstes Spiel");
        frame.setVisible(true);
        frame.setSize(1742,980);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setLayout(new BorderLayout());

        //Label
        JLabel label = new JLabel(hintergrund);
        frame.add(label, BorderLayout.CENTER);


        //Bildgrösse ändern
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                super.componentResized(e);
                hintergrund.setImage(hintergrund.getImage().getScaledInstance(e.getComponent().getWidth(), e.getComponent().getHeight(), Image.SCALE_SMOOTH));
            }
        });

    }

}
 

KonradN

Super-Moderator
Mitarbeiter
Also folgender Code funktionierte bei mir einwandfrei. Ich konnte das Fenster in der Größe anpassen und das Bild wurde dann in der Größe so angepasst, dass es immer richtig angezeigt wurde.

Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

public class GUI {

    public GUI() {

        //Bilder
        ImageIcon hintergrund = new ImageIcon(getClass().getResource("/testlevel.png"));

        //Fenster
        JFrame frame = new JFrame("Erstes Spiel");
        frame.setVisible(true);
        frame.setSize(1742,980);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setLayout(new BorderLayout());

        //Label
        JLabel label = new JLabel(hintergrund);
        frame.add(label, BorderLayout.CENTER);


        //Bildgrösse ändern
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                super.componentResized(e);
                hintergrund.setImage(hintergrund.getImage().getScaledInstance(e.getComponent().getWidth(), e.getComponent().getHeight(), Image.SCALE_SMOOTH));
            }
        });

    }

    public static void main(String[] args) {
        new GUI();
    }

}
Unterschiede zu Deinem Code:
  • frame Variable ist vom Typ JFrame. java.awt.Frame kennt keine setDefaultCloseOperation.
  • Ich habe das Bild als Ressource geladen - etwas, das Du auch machen solltest.

Bist Du sicher, dass Dein Code richtig übersetzt wurde? Ggf. hast Du immer einen alten Stand ausgeführt, weil der aktuelle Stand nicht übersetzt werden kann?

Wobei noch anzumerken ist: Du behältst das Seitenverhältnis bei dem Code nicht bei, d.h. du bekommst alles recht verzerrt. Das macht es natürlich nicht ganz so schön.


Edit: Code vergessen einzufügen :)
 

Pianoman.20

Mitglied
Ich versichere dir das diesen Code 1 zu 1 so bei mir eingetippt habe. Ich habe nun auch deine Empfehlung umgesetzt das Bild als Ressource zu laden. Ausserdem habe ich noch das Bild von einem jpg in ein png konvertiert. Tortz all dem bleibt das Problem immer noch bestehen. :/

Ich bedanke mich trotzdem für deine Bemühungen.

Noch eine Frage: Was für eine IDE verwendest du?
 

Robert Zenz

Top Contributor
Ich versichere dir das diesen Code 1 zu 1 so bei mir eingetippt habe. Ich habe nun auch deine Empfehlung umgesetzt das Bild als Ressource zu laden. Ausserdem habe ich noch das Bild von einem jpg in ein png konvertiert. Tortz all dem bleibt das Problem immer noch bestehen. :/
Kann ich bestaetigen, auf meinem Linux mit Java 8 sehe ich auch das, das Bild aendert sich nicht.

Das neusetzen auf das JLabel funktioniert:

Java:
label.setIcon(new ImageIcon(hintergrund.getImage().getScaledInstance(e.getComponent().getWidth(), e.getComponent().getHeight(), Image.SCALE_SMOOTH)));

Ich glaube das hat aber auch etwas mit der Java Version zu tun. Und aus irgendeinem Grund ist bei meinem Bild die Skalierung falsch, bin mir aber nicht sicher wieso, vermutlich die Einstellung am JLabel.
 

KonradN

Super-Moderator
Mitarbeiter
Öhm, was für ein Setup verwendet Ihr genau? Und Frame ist doch auch bei euch dann java.awt.Frame?
Frame (Java SE 17 & JDK 17) (oracle.com)
==> keine setDefaultCloseOperation Methode.

Java 8 war genannt: Frame (Java Platform SE 8 ) (oracle.com)
==> Da sehe ich auch keine setDefaultCloseOperation.

Daher wundere ich mich weiterhin, wie der Code bei euch übersetzen kann. Was übersehe ich bitte?

Und ich verwendet als Entwicklungsumgebung IntelliJ, derzeit mit openjdk 21 als aktives JDK auf meinem System. In dem Maven Projekt setzt ich aber immer noch die Version auf 17.

Ich habe das alles aber jetzt auch mal mit OpenJDK 8 gemacht und es funktioniert immer noch einwandfrei.

Ich setze hier auf einen Mac mit M1 Prozessor. Ich kann es aber auch gerne noch einmal unter Windows probieren.
 

Robert Zenz

Top Contributor
Nehme ich zurueck, es funktioniert, es ist nur so langsam dass es mir nicht aufgefallen ist dass es funktioniert. Meine Loesung geht schneller, aber wieso dein Code langsam ist, weisz ich nicht.
 

KonradN

Super-Moderator
Mitarbeiter
Ok, das beruhigt mich erst einmal ... war gerade am zweifeln, weil ich dachte, dass der ursprüngliche Code geht ...

Unter Windows funktioniert es nicht. Das Problem ist, dass er ständig neue componentResized Events erzeugt und sich so selbst zumüllt. Unter Windows (und vermutlich auch Linux) muss das setzen der Bildgröße tatsächlich wieder ein solches Event triggern... Das müllt die Queue so voll, dass er nicht mehr zeitig zu repaint kommt ... auch Fenster zu clicken wird nicht bearbeitet ...

Mal ein Code, der dann jetzt auch funktioniert - habe ein paar Ausgaben eingefügt (Debugging für Doofe :) ) und einfach mal eine mögliche Lösung eingebaut, die dann eben nichts macht, wenn die Größe unverändert ist. Also mehr ein Workaround: Das zusätzliche Event kommt aber dann interessiert es nicht, da wir nichts machen, was wieder ein Event triggern würde....

Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

public class GUI {


    JFrame frame;
    JLabel label;

    int width, height;

    public GUI() {

    width = -1;
        height = -1;

        //Bilder
        ImageIcon hintergrund = new ImageIcon(getClass().getResource("/testlevel.png"));

        //Fenster
        frame = new JFrame("Erstes Spiel");
        frame.setVisible(true);
        frame.setSize(1742,980);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setLayout(new BorderLayout());

        //Label
        label = new JLabel(hintergrund);
        frame.add(label, BorderLayout.CENTER);


        //Bildgrösse ändern
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                System.out.println("componentResized");
                super.componentResized(e);
                if (e.getComponent().getWidth() != width || e.getComponent().getHeight() != height) {
                    hintergrund.setImage(hintergrund.getImage().getScaledInstance(e.getComponent().getWidth(), e.getComponent().getHeight(), Image.SCALE_SMOOTH));
                    width = e.getComponent().getWidth();
                    height = e.getComponent().getHeight();
                }
        SwingUtilities.invokeLater(() -> printSizes());
          
            }
        });

    }

    public void printSizes() {
      System.out.println("JFrame: " + frame.getWidth() + ", " + frame.getHeight());
      System.out.println("label: " + label.getWidth() + ", " + label.getHeight());
    }

    public static void main(String[] args) {
        new GUI();
    }

}

Was mich jetzt auch irritiert: Das Verhalten ist anders. Auf dem Mac war das Bild immer ganz zu sehen. Jetzt unter Windows bleibt das Ratio zwischen Höhe und Breite gleich und ich bekomme einen Hellen Streifen oben oder unten ...

Unter Windows hatte ich jetzt Java 11 und ein Notepad als Editor.
 

Robert Zenz

Top Contributor
Unter Windows funktioniert es nicht. Das Problem ist, dass er ständig neue componentResized Events erzeugt und sich so selbst zumüllt. Unter Windows (und vermutlich auch Linux) muss das setzen der Bildgröße tatsächlich wieder ein solches Event triggern... Das müllt die Queue so voll, dass er nicht mehr zeitig zu repaint kommt ... auch Fenster zu clicken wird nicht bearbeitet ...
Haette ich mir denken koennen.

Was mich jetzt auch irritiert: Das Verhalten ist anders. Auf dem Mac war das Bild immer ganz zu sehen. Jetzt unter Windows bleibt das Ratio zwischen Höhe und Breite gleich und ich bekomme einen Hellen Streifen oben oder unten ...
Linux+Java 8 ist das vergroeszern/verkleinern "eines" zu spaet, also das Bild hat immer die Groesze vom Fenster von vor einem Vorgang.
 

KonradN

Super-Moderator
Mitarbeiter
Linux+Java 8 ist das vergroeszern/verkleinern "eines" zu spaet, also das Bild hat immer die Groesze vom Fenster von vor einem Vorgang.
Ich meine nicht. Das schien mir passend und das Bild wurde nicht verzerrt. Aber das schaue ich mir morgen noch mal genauer an, wenn ich wieder am Firmenrechner bin. (So ich die Zeit dazu finde)
 

KonradN

Super-Moderator
Mitarbeiter
Guten Morgen,

ich habe mir das unter Windows noch etwas angeschaut und das Kernproblem ist, dass es unter Windows schlicht zu viele Events gibt. Dazu einfach einmal ein Frame nehmen und nur die Events ausgeben. e.paramString() reicht schon aus. Also minimal und sonst nichts machen. Und schon sieht man:
Code:
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Event: COMPONENT_RESIZED (600,108 1579x980)
Beispiel von meinem System. Es wurden hier also u.a. 9 solcher Events nacheinander erzeugt.

Da der Code nun eine Bildverarbeitung macht und ein Bild erzeugt, wird das natürlich von der Verarbeitung schwer. Es müssen ja intern immer neue Images erzeugt werden (getScaledImage Aufruf), was vermutlich zu einem enormen Speicherdruck führt und dann neben dem Image erzeugen auch den GC triggern wird. Das müsste man noch verifizieren - ist also erst eine reine Vermutung.

Auf dem Mac habe ich das Problem etwas weniger, denn dort bekomme ich diese zusätzlichen Events nicht.

Die Lösung wäre jetzt also aus meiner Sicht, die Last bei der Aktion zu minimieren. Was man da machen kann, wäre ein Check der Größe, die ich schon vorgeschlagen habe. Das ist also eine Option. Aber da immer noch relativ viele Events kommen wenn man das Fenster mit der Maus in der Größe verändert (auch auf dem Mac habe ich bei einem Vorgang mehrere Events - aber halt nur keine doppelten pro Größe), ist eine Idee, dass man zeitverzögert agiert.

Swing hat einen Timer und den nutzen wir dann einfach einmal dafür. Wir suchen uns eine Verzögerung aus - das kann z.B. 100 ms sein.
In dem Event prüfen wir dann nur, ob der Timer bereits läuft. Sollte er noch nicht laufen, dann starten wir ihn.
Und wenn der Timer abgelaufen ist, dann machen wir den Resize des Bildes.

Dann ist das Bild immer mehr verzerrt - daher merken wir uns das Bild direkt nach dem laden und nutzen dieses gemerkte Bild für alle Scalings.

Und der Verdacht, dass wir bei der Größe immer hinterher hinken: Das prüfen wir auch. Dazu machen wir einfach einen Timer, der jede Sekunde die Framegröße ausgibt. Dann können wir sehen, ob die Größe wirklich hinterher hinkt bei den Events. (Edit: Was ich nicht bestätigen konnte. Im Rahmen meiner Tests hatte ich aber auch teilweise diesen Eindruck. Kann ich gerade aber nicht erklären zumal ich den Zustand nicht mehr habe und den genauen Code zu dem Zeitpunkt).

Das ergibt dann folgenden Code:
Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

public class GUI {

    public static final int REFRESH_TIMEOUT = 100;

    JFrame frame;
    JLabel label;
    Timer timer;
    ImageIcon hintergrund;
    Image image;

    public GUI() {

        //Bilder
        hintergrund = new ImageIcon(getClass().getResource("/testlevel.png"));
        image = hintergrund.getImage();

        //Fenster
        frame = new JFrame("Erstes Spiel");
        frame.setSize(1742,980);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setLayout(new BorderLayout());

        //Label
        label = new JLabel(hintergrund);
        frame.add(label, BorderLayout.CENTER);


        timer = new Timer(REFRESH_TIMEOUT, e -> doResize());
        timer.setRepeats(false);
        timer.setCoalesce(false);

        Timer sizeReporter = new Timer(1000, e -> printSize());
        sizeReporter.setRepeats(true);
        sizeReporter.start();

        //Bildgrösse ändern
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                System.err.println("Event: " + e.paramString() + " Frame Size: " + frame.getWidth() + "x" + frame.getHeight());
                if (!timer.isRunning()) {
                    timer.restart();
                }
            }
        });

        frame.setVisible(true);
    }

    public void printSize() {
        System.err.println("Frame Size: " + frame.getWidth() + "x" + frame.getHeight());
    }

    public void doResize() {
        System.err.println("Frame Resize to " + frame.getWidth() + "x" + frame.getHeight());
        hintergrund.setImage(image.getScaledInstance(frame.getWidth(), frame.getHeight(), Image.SCALE_SMOOTH));
        frame.repaint();
    }

    public static void main(String[] args) {
        new GUI();
    }

}

(Habe noch die Frame Größe beim Event mit ausgegeben. Das Frame hat also die Größe, die im Event angegeben wird bei mir. Da läuft also nichts durcheinander ... )

Das einfach einmal als Idee, nach dem man mal eine Nacht drüber geschlafen hat :)

Edit: Die Prüfung des "hinterher Hinkens" hatte ich vergessen zu erwähnen. Der Code funktioniert bei mir auf Windows einwandfrei.
 

KonradN

Super-Moderator
Mitarbeiter
Wobei das so aussieht, dass es da unter Linux auch keine mehrfachen Events für eine Auflösung gibt. Die Zahlen bei COMPONENT_RESIZED scheinen alle unterschiedlich zu sein (wobei das nicht 100% auf dem Bildschirmfoto zu sehen ist). Das ist dann ggf. doch eine Thematik die speziell Windows betreffen könnte...

Das war der Punkt, den ich evtl. schlecht dargestellt hatte: Bei Windows hatte ich deutlich mehr Events ... und ich habe da nur die Events von einer Auflösung heraus kopiert... Also nicht nur, dass ich in kurzer Zeit eine Hand voll Events bekommen habe, weil ich halt mit der Maus das Fenster in der Größe verändert habe, sondern Windows hat sich den Spaß erlaubt, mir gleich noch ein vielfaches an Events zu geben....

Das evtl. nur noch zur Verdeutlichung des gefundenen Problems bei meinem Windows System.

Und natürlich: Danke M.L. fürs testen und berichten!
 

Pianoman.20

Mitglied
Hallo Konrad,

Danke vielmals, nun läuft der Code wie am Schnürchen!

Nur leider verstehe ich Ihn nicht so ganz. Könntest du mir nochmals in Kurzform die einzelnen Abschnitte im Code erklären?
 

KonradN

Super-Moderator
Mitarbeiter
Nur leider verstehe ich Ihn nicht so ganz. Könntest du mir nochmals in Kurzform die einzelnen Abschnitte im Code erklären?
Schauen wir uns den Code einfach einmal im Detail an:
Java:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

public class GUI {

    public static final int REFRESH_TIMEOUT = 100;

    /**
     * Hauptfenster
     */
    private JFrame frame;

    /**
     * Label, welches das Bild anzeigt
     * ==> Tipp: Sollte dann auch so benannt sein. pictureLabel, backgroundLabel, ...
     */
    JLabel label;

    /**
     * Timer für verzögertes Anpassen des Hintergrunds
     * ==> Tipp: Sollte auch einen vernünftigen Namen haben. delayedDrawTimer oder so.
     */
    Timer timer;

    /**
     * Das ImageIcon für den Hintergrund.
     */
    ImageIcon hintergrund;

    /**
     * Das Hintergrundbild in original Größe.
     * Wir wollen immer das original Bild verwenden um unnötige Verzerrungen zu vermeiden
     * ==> Auch hier fehlt ein vernünftiger Name!
     */
    Image image;

    /**
     * Creates a new instance of GUI
     * <remark>
     *     Creates and shows the main window.
     * </remark>
     */
    public GUI() {

        // Laden des ImageIcon mit dem Bild aus den Ressourcen.
        // Damit kann das Bild auch im Jar file sein.
        hintergrund = new ImageIcon(getClass().getResource("/testlevel.png"));

        // Wir holen und das Bild und speichern es in der Variable für wiederholte Nutzung.
        image = hintergrund.getImage();

        //Fenster
        frame = new JFrame("Erstes Spiel");
        frame.setSize(1742,980);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setLayout(new BorderLayout());

        //Label
        label = new JLabel(hintergrund);
        frame.add(label, BorderLayout.CENTER);

        // Wir erstellen uns einen Swing Timer. Dieser wird auf dem UI Thread ausgeführt, somit
        // gibt es keine Probleme mit mehreren Threads. (Das wäre beim java.util.Timer evtl. der Fall!)
        // Der Timer soll, wenn gestartet, nur einmal laufen. Das zweite ist vermutlich nicht notwendig.
        // Im Timer habe ich eine Lambda Expression genutzt, die doResize aufruft.
        timer = new Timer(REFRESH_TIMEOUT, e -> doResize());
        timer.setRepeats(false);
        timer.setCoalesce(false);

        // Dies ist einfach nur einw Debugging Hilfe. Wir wollen ständig die Größe des Fensters ausgeben.
        // Das kannst Du komplett löschen.
        Timer sizeReporter = new Timer(1000, e -> printSize());
        sizeReporter.setRepeats(true);
        sizeReporter.start();

        // Hier kommt der ComponentListener.
        frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                // Eine Ausgabe (err, weil das ungepuffert ist)
                // Die sollte weg!
                System.err.println("Event: " + e.paramString() + " Frame Size: " + frame.getWidth() + "x" + frame.getHeight());

                // Wir prüfen, ob der Timer bereits läuft.
                // Wenn es also schon ein Event gab und der Timer noch nicht ausgelöst hat,
                // dann wird das Event ignoriert.
                if (!timer.isRunning()) {
                    // Wenn der Timer nicht läuft, dann wird er neu gestartet.
                    timer.restart();
                }
            }
        });

        // Am Ende machen wir das Fenster sichtbar.
        // Wichtig: Dies sollte immer am Ende erfolgen!
        frame.setVisible(true);
    }

    /**
     * Einfache Ausgabe der Größe des Fensters.
     */
    public void printSize() {
        System.err.println("Frame Size: " + frame.getWidth() + "x" + frame.getHeight());
    }

    /**
     * Verändern der Bildgröße.
     */
    public void doResize() {
        // Die Ausgabe sollte herausgenommen werden.
        System.err.println("Frame Resize to " + frame.getWidth() + "x" + frame.getHeight());

        // Hier wird der Hintergrund gesetzt.
        // Was mir gerade auffällt: frame.getWidth() und frame.getHeight() sind nicht der sichtbare Bereich und
        // daher vermutlich nicht ganz korrekt. frame.getRootPane().getWidth() bzw. getHeight() dürften besser sein.
        // Einfach mal selbst schauen, ob ein Rand vom Bild nicht zu sehen ist. Also unten noch einen Strich
        // machen oder so und schauen, ob der zu sehen ist.
        hintergrund.setImage(image.getScaledInstance(frame.getWidth(), frame.getHeight(), Image.SCALE_SMOOTH));

        // Ups. Das ist ein Überbleibsel meiner Tests. Das darf gehen! Du brauchst kein explizites repaint!
        // Ein setImage auf ImageIcon führt selbstverständlich auch selbst ein repaint auf dem ImageIcon aus.
        // Dieser Aufruf kann also weg!
        frame.repaint();
    }

    /**
     * Main method to start the application.
     * @param args Unused application arguments.
     */
    public static void main(String[] args) {
        new GUI();
    }

}

Bitte die Kommentare beachten. Mir sind halt direkt noch ein paar Dinge aufgefallen:
a) Die Namen der Variablen sind teilweise unschön. Da hatte ich so früh am Morgen nicht drauf geachtet.
b) Im doResize wird aus meiner Sicht die falsche Größe verwendet. Statt der Größe vom Frame muss es vermutlich die Größe der RootPane sein. Das solltest Du einmal ausprobieren / schauen.
 

KonradN

Super-Moderator
Mitarbeiter
Zu b)
Sobald Du im BorderLayout noch weitere Dinge nutzt, dann wird die RootPane auch größer sein. Da wäre dann eine eigene Pane notwendig, dessen Größe Du dann nehmen könntest meine ich.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
steven789hjk543 Swing Weiß jemand, warum dieses Programm nicht funktioniert? AWT, Swing, JavaFX & SWT 7
A Swing Programm funktioniert aber zwei Buttons werden angezeigt AWT, Swing, JavaFX & SWT 3
Juelin starten maven javafx programm ohne netbeans AWT, Swing, JavaFX & SWT 38
G CAD Programm AWT, Swing, JavaFX & SWT 4
thor_norsk E - Mail Programm AWT, Swing, JavaFX & SWT 2
_user_q Über installDist exportiertes Programm wirft "Unsupported JavaFX configuration" AWT, Swing, JavaFX & SWT 0
CodingBerlin JavaFX Programm läuft nur unter Eclipse AWT, Swing, JavaFX & SWT 1
J Programm findet Resource nicht. AWT, Swing, JavaFX & SWT 6
N Programm Läuft nicht auf anderen Pcs AWT, Swing, JavaFX & SWT 9
imawake Java Paket-Tracking Programm 📦 AWT, Swing, JavaFX & SWT 7
S Lustiges programm schnell coden? AWT, Swing, JavaFX & SWT 2
O Ein Java-Programm mit Swing steuern AWT, Swing, JavaFX & SWT 1
P Swing Programm hängt sich bei Buttondruck auf? (GUI für "Chatbot" erstellen) AWT, Swing, JavaFX & SWT 15
M DragAndDrop - aus Browser ins Programm AWT, Swing, JavaFX & SWT 6
T Java GUI - Würfel Programm AWT, Swing, JavaFX & SWT 6
JojoSand Java Programm wird nicht gestartet - keinen Fehlerhinweis AWT, Swing, JavaFX & SWT 9
SvenPittelkow Programm soll auf JButton warten bis der geklickt wurde AWT, Swing, JavaFX & SWT 1
I Gui in bestehendes Java-Programm AWT, Swing, JavaFX & SWT 11
L JavaFX IntelliJ Standalone Programm exportieren AWT, Swing, JavaFX & SWT 9
Bluedaishi JavaFX Programm start mit zwei scenen bzw Fenster AWT, Swing, JavaFX & SWT 1
M AWT Programm in den Tray minimieren AWT, Swing, JavaFX & SWT 2
A Java Programm gestalten AWT, Swing, JavaFX & SWT 4
P Swing Warum startet das Programm nicht? AWT, Swing, JavaFX & SWT 3
J JavaFX Kamera im Programm öffnen AWT, Swing, JavaFX & SWT 6
H Hintergrundbild in einem Programm AWT, Swing, JavaFX & SWT 4
MR._FIRE_Flower progressBar in ein bestehendes Programm einbauen AWT, Swing, JavaFX & SWT 3
J Verbesserungsvorschläge?! Kleines AWT Programm AWT, Swing, JavaFX & SWT 4
A Programm nun in ein Frame basteln AWT, Swing, JavaFX & SWT 1
G Zeichnen Programm AWT, Swing, JavaFX & SWT 1
R Swing Programm läuft nur beim Debuggen korrekt ab AWT, Swing, JavaFX & SWT 4
Arif Swing Programm friert ein... AWT, Swing, JavaFX & SWT 2
apple_pie1998 Programm gibt plötzlich exceptions aus. AWT, Swing, JavaFX & SWT 4
N Swing Wie Programm strukturieren? (Dynamisch Komponenten hinzufügen) AWT, Swing, JavaFX & SWT 1
N JLabel ändern während Programm ausgeführt wird AWT, Swing, JavaFX & SWT 4
X JMenu Menu-Bar ausrichtung ändern im Programm AWT, Swing, JavaFX & SWT 0
T Swing Programm hängt sich auf! AWT, Swing, JavaFX & SWT 23
L Probleme mit Programm AWT, Swing, JavaFX & SWT 13
S Grafik: Programm kommt nicht zur paint()-Methode AWT, Swing, JavaFX & SWT 6
P Programm durch Dateiaufruf starten und Datei öffnen AWT, Swing, JavaFX & SWT 2
S Action durchführen beim Programm beenden AWT, Swing, JavaFX & SWT 3
N Programm mit Swing und Thread, Figur bewegen sich nicht AWT, Swing, JavaFX & SWT 6
G Programm GUI erstellen AWT, Swing, JavaFX & SWT 5
L JButton flackern - Programm hängt sich auf AWT, Swing, JavaFX & SWT 3
T Programm im METRO Design? AWT, Swing, JavaFX & SWT 1
J Programm hängt sich bei Log In auf AWT, Swing, JavaFX & SWT 1
C Programm mit Passwort schließen AWT, Swing, JavaFX & SWT 5
D Robot Programm mit Escape abbrechen (aus Eclipse heraus) AWT, Swing, JavaFX & SWT 2
N Programm läuft perfekt in Eclipse aber nicht in .JAR AWT, Swing, JavaFX & SWT 3
S JScrollPane --> Programm hängt sich beim scrollen auf AWT, Swing, JavaFX & SWT 2
J Programm im GUI ausführen AWT, Swing, JavaFX & SWT 4
A Probleme mit 2 JFrames in einem Programm AWT, Swing, JavaFX & SWT 7
S Event Handling konsole Programm mit Tastendruck beenden AWT, Swing, JavaFX & SWT 5
G JTextArea on the fly aus anderem Programm befüllen AWT, Swing, JavaFX & SWT 4
K Swing Konsolen Programm in GUI - Suche Hilfe bei Konsolenausgabe AWT, Swing, JavaFX & SWT 2
W Windows Fenster in einem Programm AWT, Swing, JavaFX & SWT 2
F Mal Programm AWT, Swing, JavaFX & SWT 13
C In Hauptfenster Programm-Klassen implementieren AWT, Swing, JavaFX & SWT 9
A KeyListener Button wechseln & Programm beenden AWT, Swing, JavaFX & SWT 4
C Swing Wie kann ich im Programm das Menü schließen AWT, Swing, JavaFX & SWT 2
J SWING - Programm ohne Eclipse etc. starten AWT, Swing, JavaFX & SWT 7
J Button soll nach Klick Text ändern und Programm in 3 Sekunden beenden AWT, Swing, JavaFX & SWT 6
M Programm hängt sich auf nachdem repaint() benutzt wurde AWT, Swing, JavaFX & SWT 2
M Programm in Jframe "einbinden" AWT, Swing, JavaFX & SWT 7
S Programm auf aktuellen Stand bringen AWT, Swing, JavaFX & SWT 2
Spin Tool: Popup -Programm AWT, Swing, JavaFX & SWT 3
GUI-Programmer Swing JWindow - Programm wird ohne Grund beendet AWT, Swing, JavaFX & SWT 3
E Kreis-Mal-Programm AWT, Swing, JavaFX & SWT 7
P Programm aktuallisiert sich nur beim Rüberzeigen AWT, Swing, JavaFX & SWT 6
T Java-Anwendung arbeitet Programm in seltsamer Reihenfolge ab AWT, Swing, JavaFX & SWT 3
C Programm für 5 sekunden anhalten und actionevent abfangen AWT, Swing, JavaFX & SWT 18
J Programm schließen AWT, Swing, JavaFX & SWT 4
J Programm nur einmal öffnen AWT, Swing, JavaFX & SWT 3
E Windows Kontext Menü - Laufendes Programm AWT, Swing, JavaFX & SWT 3
T Programm vom Desktop starten! AWT, Swing, JavaFX & SWT 2
S Programm pausieren während JDialog offen ist AWT, Swing, JavaFX & SWT 2
M Browser in Programm einbinden AWT, Swing, JavaFX & SWT 4
R Durch Listener lässt sich Programm nicht mehr ausführen AWT, Swing, JavaFX & SWT 4
H Userinput in GUI an externes Programm AWT, Swing, JavaFX & SWT 8
D Firefox aus Programm starten AWT, Swing, JavaFX & SWT 4
S LayoutManager Einfaches Programm mit VE und Eclipse3.6 realisieren AWT, Swing, JavaFX & SWT 2
J SWT 32 bit Programm für 64 bit Jave AWT, Swing, JavaFX & SWT 10
R GUI hängt während Programm läuft AWT, Swing, JavaFX & SWT 7
S AWT Fenster schließen und Programm beenden AWT, Swing, JavaFX & SWT 10
H Infofenster vor dem eigentlichen Java Programm AWT, Swing, JavaFX & SWT 3
B Uberschrift in Swing-Programm zur Laufzeit aendern... AWT, Swing, JavaFX & SWT 4
C Swing Kleines Programm mit SWING unter Verwendung von MVC AWT, Swing, JavaFX & SWT 5
capgeti Wie Programm in JPanel (o.ä.) starten? AWT, Swing, JavaFX & SWT 3
B Externes Programm mit Robot Steuern AWT, Swing, JavaFX & SWT 3
G Programm läuft nur auf manchen Rechnern AWT, Swing, JavaFX & SWT 10
M SWT Programm pausiert ungewollt AWT, Swing, JavaFX & SWT 5
K Website in Java Programm einbinden AWT, Swing, JavaFX & SWT 3
B komplettes programm in einem fenster AWT, Swing, JavaFX & SWT 5
L Frage bei Programm mit Zufallskreisen AWT, Swing, JavaFX & SWT 9
L Programm terminated (zu frueh) AWT, Swing, JavaFX & SWT 3
T SplashScreen im laufenden Programm AWT, Swing, JavaFX & SWT 2
M Panelinhalt im Programm verändern AWT, Swing, JavaFX & SWT 12
R Programm nicht mehr in der Taskleiste AWT, Swing, JavaFX & SWT 2
3TageBart Ebenen in Zeichen-Programm implementieren AWT, Swing, JavaFX & SWT 9
H Swing HUD in externes Programm einblenden AWT, Swing, JavaFX & SWT 7
R Japaner koennen mein Programm nicht nutzen..? AWT, Swing, JavaFX & SWT 11

Ähnliche Java Themen

Neue Themen


Oben