简体   繁体   中英

How to resize a JFrame relative to maximized size

I am making a game in java, and I need to figure out how to resize the JFrame relative to the maximized size. After reading Hovercraft full of Eel's responses and playing with it some more, I have updated the code. This should be a minimal, complete and verifiable example:

import java.awt.Dimension;
import javax.swing.*;
import java.awt.Toolkit;
import java.lang.reflect.InvocationTargetException;

public class MyFrameTest {
    JFrame frame;

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

    public MyFrameTest() {
        System.out.println("screenSize: " + Toolkit.getDefaultToolkit().getScreenSize());

        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    frame = new JFrame();
                    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                    frame.setVisible(true);
                }
            });
        }
        catch (Exception e) { System.out.println("invokeAndWait exception: "+e);  }

        set_size(300,300);
        set_size(500,500);
        set_size(0,0);
        set_size(0,0);
        set_size(500,500);
        set_size(0,0);
        set_size(0,0);
        System.exit(0);
    }

    public void set_size(int width, int height) {
        final Dimension d = new Dimension(width, height);
        final int w = width;
        final int h = height;
        try { Thread.sleep(500); } catch (InterruptedException e) { }

        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    if (w == 0 && h == 0) {
                        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                    } else {
                        frame.setExtendedState(JFrame.NORMAL);
                        frame.setSize(d);
                    }
                    displaySize(frame, w, h);
                }
            });
        } catch (Exception e) { System.out.println("invokeAndWait exception: "+e);  }
    }
    public void displaySize(JFrame frame, int width, int height) {
            Dimension df = frame.getSize(); 
            Dimension dc = frame.getContentPane().getSize(); 
            if (width == 0 && height == 0)
                System.out.printf("Size maximized : Frame %4d,%-4d : Content %4d,%-4d\n",
                     df.width, df.height, dc.width, dc.height);
            else
                System.out.printf("Size %4d,%-4d : Frame %4d,%-4d : Content %4d,%-4d\n",
                     width, height, df.width, df.height, dc.width, dc.height);
    }
}

I am developing on Linux, and this is the output I get:

screenSize: java.awt.Dimension[width=1920,height=1080]
Size  300,300  : Frame  300,300  : Content    2,1   
Size  500,500  : Frame  500,500  : Content  292,270 
Size maximized : Frame  500,500  : Content  492,470 
Size maximized : Frame 1928,1033 : Content 1920,1003
Size  500,500  : Frame  500,500  : Content 1920,1003
Size maximized : Frame  500,500  : Content  492,470 
Size maximized : Frame 1928,1033 : Content 1920,1003

When I set it to a non-maximized size, the frame size is correct, but if I make it maximized, the first time I check the frame size it shows no difference. It only shows up the second time I set it and query it.

Weirdly, if I set it twice in a row like this

            if (w == 0 && h == 0) {
                frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
            } else {

then it NEVER updates the frame size no matter how many times I called it. If I set it three times in a row it works. Maybe it is a toggle?

Even weirder, the ContentPane size lags the frame size, which means it lags the maximized size by TWO tries.

While playing with the program I managed to get one combination that was behaving randomly on successive runs, sometimes it updates immediately and sometimes it does not. This makes me think maybe there is a thread timing thing going on in the EDT, maybe it is a lazy update, but adding a longer sleep does not help.

On Windows the output is:

screenSize: java.awt.Dimension[width=1600,height=900]
Size  300,300  : Frame  300,300  : Content  116,0
Size  500,500  : Frame  500,500  : Content  284,262
Size maximized : Frame 1616,854  : Content  484,462
Size maximized : Frame 1616,854  : Content 1600,816
Size  500,500  : Frame  500,500  : Content 1600,816
Size maximized : Frame 1616,854  : Content  484,462
Size maximized : Frame 1616,854  : Content 1600,816

Here the JFrame size is correct but the ContentPane still lags the frame.

When I run this code though, I do not get the expected result:

It is the expected result because you're calling the methods before the JFrame has been rendered, when its size is in fact 0, 0. As for the preferredSize -- hard to say without more information. Try printing this out after rendering.

For example:

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

public class MyFrameTest {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                final JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                displaySizes(frame, "Before");

                frame.setVisible(true);

                // to place this onto the event queue and so delay its calling until after
                // the JFrame has been rendered
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        displaySizes(frame, "After");
                    }
                });
            }

            private void displaySizes(final JFrame frame, String when) {
                Dimension size = frame.getContentPane().getSize();
                Dimension preferredSize = frame.getContentPane().getPreferredSize();
                System.out.println(when + " Rendering");
                System.out.println("Size:      " + size);
                System.out.println("Pref Size: " + preferredSize);
            }
        });
    }
}

Which outputs:

Before Rendering
Size:      java.awt.Dimension[width=0,height=0]
Pref Size: java.awt.Dimension[width=0,height=0]
After Rendering
Size:      java.awt.Dimension[width=1680,height=988]
Pref Size: java.awt.Dimension[width=0,height=0]

Note that the preferredSize has nothing to do with the rendered size and all to do with the components added and layout managers used.

I don't think that a content pane works like that. I could be wrong, but I prefer to use my own drawing panel (JPanel).

I created a drawing panel and a JFrame. After creating the drawing Panel and JFrame, I set the JFrame to either NORMAL or MAXIMIZED_BOTH. This more accurately simulates a game switching from normal to maximized and back to normal.

Finally, I created a separate Runnable class for the size test. Here are the results from Windows 8.1. If anyone can run this code on Linux, I'd appreciate it.

screenSize: java.awt.Dimension[width=1600,height=900]
Frame 1616,876  : Content 1600,837 
Frame  416,439  : Content  400,400 
Frame 1616,876  : Content 1600,837 
Frame  416,439  : Content  400,400 
Frame 1616,876  : Content 1600,837 

The main differences are that I used a window state listener to detect when the JFrame size changed, and I used the preferred size of the drawing panel to get the correct content size.

The code to modify your drawing panel based on a size change of the JFrame would go in the windowStateChanged method, after the panel.setPreferredSize method call.

Here's the code:

package com.ggl.testing;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;

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

public class FrameResizeTesting implements Runnable {

    private Dimension frameSize;
    private Dimension panelSize;
    private Dimension screenSize;

    private JFrame frame;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new FrameResizeTesting());
    }

    public FrameResizeTesting() {
        this.screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        System.out.println("screenSize: " + this.screenSize);
    }

    @Override
    public void run() {
        final DrawingPanel panel = new DrawingPanel();

        frame = new JFrame("Frame Resizing Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addWindowStateListener(new WindowStateListener() {

            @Override
            public void windowStateChanged(WindowEvent event) {
                Dimension d = frame.getSize();
                d.width = d.width - frameSize.width + panelSize.width;
                d.height = d.height - frameSize.height + panelSize.height;
                panel.setPreferredSize(d);
                panel.invalidate();
                displaySize(panel);
            }

        });

        frame.add(panel);
        frame.pack();

        panelSize = panel.getSize();
        frameSize = frame.getSize();

        frame.setVisible(true);

        new Thread(new ResizingRunnable()).start();
    }

    private void displaySize(DrawingPanel panel) {
        Dimension df = frame.getSize();
        Dimension dc = panel.getPreferredSize();

        System.out.printf("Frame %4d,%-4d : Content %4d,%-4d\n", df.width,
                df.height, dc.width, dc.height);
    }

    public class DrawingPanel extends JPanel {
        private static final long serialVersionUID = 124082399133852883L;

        public DrawingPanel() {
            this.setPreferredSize(new Dimension(400, 400));
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            g.setColor(Color.RED);
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    public class ResizingRunnable implements Runnable {

        @Override
        public void run() {
            set_size(false);
            set_size(true);
            set_size(false);
            set_size(true);
            set_size(false);
            set_size(true);
            set_size(true);
            System.exit(0);
        }

        private void set_size(final boolean isExtended) {
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    if (isExtended) {
                        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                    } else {
                        frame.setExtendedState(JFrame.NORMAL);
                    }
                }
            });

        }
    }
}

If you need the MAXIMIZED size, write this

setExtendedState(JFrame.MAXIMIZED_BOTH);

If you want the NORMAL size

setExtendedState(JFrame.NORMAL);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM