windowStateChanged macht exakt das Gegenteil

Hallo zusammen

  • Wenn ich das Fenster vergrössere, dann kommt der Wert der ehemaligen (kleineren) Grösse raus

  • Wenn ich das Fenster verkleinere, dann kommt der Wert der ehemaligen (grösseren) Grösse raus


public Form(final FTPRunnable ftpRunnable) throws Exception

    {

        imageLabel = new JLabel("");

        Form.properties = (System.getProperty("user.dir") + "/settings/settings.ini").replace("\\", "/");

        this.propertyMngr = new PropertyMngr();

        this.propertyMngr.init(Form.properties, false);

        final String host = this.propertyMngr.getString("host").trim();

        final String username = this.propertyMngr.getString("username").trim();

        final String password = this.propertyMngr.getString("password").trim();

        this.ftp = new FileTransferClient();

        this.ftp.setRemoteHost(host);

        this.ftp.setUserName(username);

        this.ftp.setPassword(password);

        this.ftpRunnable = ftpRunnable;

        this.mainFrame = new JFrame();

        final Color bgcolor = new Color(Integer.parseInt("efefef", 16));

        this.mainFrame.getContentPane().setBackground(bgcolor);

        JLabel imageLabel = this.imageLabel;

        this.mainFrame.getContentPane().add(imageLabel, BorderLayout.CENTER);

        this.mainFrame.setTitle("Captured image preview");

        this.mainFrame.setBounds(100, 100, Form.APP_WIDTH, Form.APP_HEIGHT);

        this.mainFrame.setSize(Form.APP_WIDTH, Form.APP_HEIGHT);

        this.mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        mainFrame.addWindowStateListener(new WindowStateListener() {

            public void windowStateChanged(WindowEvent arg0)

            {

                windowStateChanged_mainFrame(arg0);

            }

        });

    }

    private void windowStateChanged_mainFrame(WindowEvent e)

    {

        boolean max = false;

        boolean min = false;

        if ((e.getNewState() & Frame.NORMAL) == Frame.NORMAL)

        {

            min = !min;

        }

        if ((e.getNewState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH)

        {

            max = !max;

        }

        int imageLabelWidth = imageLabel.getWidth();

        int imageLabelHeight = imageLabel.getHeight();

        if (max && min)

        {

            System.out.println("Max. window size ... ");

        }

        else

        {

            if (!max && min)

            {

                System.out.println("Smaller than max. window size ... ");

            }

        }

        System.out.println("imageLabelWidth: " + imageLabelWidth + ", imageLabelHeight: " + imageLabelHeight);

        final Image image = bufferedImage.getScaledInstance(imageLabelWidth, imageLabelHeight, Image.SCALE_SMOOTH);

        final ImageIcon imageIconNew = new ImageIcon(image);

        this.imageLabel.setIcon(imageIconNew);

    }

(Schräg ist auch, dass beim Vergrössern beide if zutreffen (if ((e.getNewState() ... ), beim Verkleinern aber nur eines. Deshalb mein Gebastel. Scheint aber zu funktionieren.)

Hm. Man kann mit Java+Swing so schönen Code schreiben :frowning:

Aber ok…

Grob gesagt: Der WindowStateListener wird „zuerst“ informiert. Da wird nur gesagt: Hey, hier hat sich was am Fensterzustand geändert. Mehr erstmal nicht. Dass da dieses imageLabel drin ist, und dass das Bild in diesem label immer so groß sein soll, wie das Label, hat damit erstmal nichts zu tun.

Nachdem die Fenstergröße geändert wurde, laufen innerhalb von Swing die ganzen Mechanismen los, die dafür sorgen, dass das Fenster irgendwann „schön“ aussieht (d.h. die LayoutManager erledigen ihren Job).

Dabei wird unter anderen die Größe des imageLabel geändert. Und das wäre der Zeitpunkt, um etwas zu machen, wofür man die Größe braucht.

Lösen könnte man das, indem man einen ComponentListener an das imageLabel hängt, der bei componentResized immer die richtige Größe abfragen kann. Das ist unten im Code mal angedeutet.

Aber: Wenn es nur darum geht, ein Bild immer so zu zeichnen, dass es das imageLabel ausfüllt, sollte man das anders machen. Im einfachsten Fall könntest/solltest du das imageLabel durch sowas hier ersetzen:

class SimpleImageComponent extends JComponent
{
    private BufferedImage image;
    
    public void setImage(BufferedImage image)
    {
        this.image = image;
    }
    
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (image != null)
        {
            g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
        }
    }
}

Das bekommt das komplette Bild, und malt es immer so groß, wie die component halt gerade ist. (Kein getScaledInstance mehr. getScaledInstance ist in vieler Hinsicht schlecht (vor allem ineffizient), und es gibt praktisch keinen Grund, warum man das noch verwenden sollte).

Natürlich stellen sich dann gleich ein paar Fragen: Soll das Bild ggf. nur vergrößert oder auch verkleinert werden? Soll das Seitenverhältnis des Bildes beibehalten werden? Wenn ja: Soll das Bild links, mittig oder rechts ausgerichtet sein? Die meisten dieser Fragen hatte ich man angegangen:

Aber hier noch das mit dem ComponentListener, auch wenn das sicher nicht die sinnvollste Lösung ist:

package bytewelt;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class WindowStateTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui()
    {
        WindowStateTest t = new WindowStateTest();
        t.getMainFrame().setVisible(true);
    }

    private JFrame mainFrame;
    private JLabel imageLabel;

    private ComponentListener imageLabelSizeListener = new ComponentAdapter()
    {
        @Override
        public void componentResized(ComponentEvent e)
        {
            System.out.println("Label size is now "+imageLabel.getSize());
        }
    };
    
    public WindowStateTest()
    {
        mainFrame = new JFrame();
        
        imageLabel = new JLabel("Boo!");
        imageLabel.addComponentListener(imageLabelSizeListener);
        
        mainFrame.getContentPane().add(imageLabel, BorderLayout.CENTER);

        mainFrame.setTitle("Captured image preview");
        mainFrame.setBounds(100, 100, 500, 500);
        mainFrame.setSize(500, 500);
        mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        mainFrame.addWindowStateListener(new WindowStateListener()
        {
            public void windowStateChanged(WindowEvent arg0)
            {
                windowStateChanged_mainFrame(arg0);
            }
        });
    }
    
    
    JFrame getMainFrame()
    {
        return mainFrame;
    }

    private void windowStateChanged_mainFrame(WindowEvent e)
    {
        boolean max = false;
        boolean min = false;

        if ((e.getNewState() & Frame.NORMAL) == Frame.NORMAL)
        {
            min = !min;
        }
        if ((e.getNewState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH)
        {
            max = !max;
        }

        int imageLabelWidth = imageLabel.getWidth();
        int imageLabelHeight = imageLabel.getHeight();
        if (max && min)
        {
            System.out.println("Max. window size ... ");
        }
        else
        {
            if (!max && min)
            {
                System.out.println("Smaller than max. window size ... ");
            }
        }
        System.out.println("imageLabelWidth: " + imageLabelWidth
            + ", imageLabelHeight: " + imageLabelHeight);

    }
}

Vielen Dank, das hat wunderbar geklappt mit addComponentListener / new ComponentAdapter() ... / public void componentResized(ComponentEvent e) sowie dem BufferedImage: bufferedImage.getScaledInstance(imageLabelWidth, imageLabelHeight, Image.SCALE_SMOOTH);

„Hm. Man kann mit Java+Swing so schönen Code schreiben“

Ja, könnte man, was ich auch versuche. Ist aber schon ca. 12 Jahre her als ich das letzte Java-GUI-Programm geschrieben habe. (Damals mit EasyEclipse und SWT)

Aber Swing ist im Prinzip ja relativ einfach zu verstehen, das Einarbeiten geht relativ locker. (Kein Vergleich z.B. mit dem Erstellen von Web-GUIs mit Angular, woran ich mir immer wieder die Zähne ausbeisse…)