如何相对于最大化的大小调整JFrame窗口的大小

3

我正在使用Java制作一个游戏,需要弄清如何相对于最大化大小调整JFrame的大小。在阅读了Hovercraft full of Eel的回答并进行了一些实验后,我已经更新了代码。以下应该是一个最小、完整和可验证的示例:

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);
    }
}

我正在Linux上开发,这是我得到的输出:

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

当我设置为非最大化大小时,框架的大小是正确的,但如果我将其最大化,第一次检查框架大小时不会显示任何差异。只有第二次设置和查询时才会显示出来。
奇怪的是,如果我连续两次设置它如下:
            if (w == 0 && h == 0) {
                frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
                frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
            } else {

无论我调用多少次,它都从不更新框架大小。如果我连续设置三次,它就有效了。也许这是一个切换开关?

更奇怪的是,ContentPane 大小滞后于框架大小,这意味着它滞后于最大化大小两个尝试。

在操作程序时,我设法获得了一种组合,在连续运行时表现随机,有时会立即更新,有时则不会。这使我认为在 EDT 中可能存在线程时间问题,也许它是懒惰更新,但添加更长的休眠并没有帮助。

在 Windows 上输出如下:

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

在这里JFrame的大小是正确的,但是ContentPane仍然落后于框架。
3个回答

1
当我运行这段代码时,结果并非预期:
这是因为您在JFrame渲染之前调用了方法,此时其大小实际上为0,0。至于preferredSize-没有更多信息很难说。尝试在渲染后打印出来。
例如:
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);
            }
        });
    }
}

输出结果如下:

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]

请注意,preferredSize与渲染大小无关,而与添加的组件和使用的布局管理器有关。

我尝试过了,但现在getSize()返回-10,而getPreferredSize仍然返回0。 - mooncat39
@mooncat39:请参考上面的示例。请注意,preferredSize与呈现大小无关,而与添加的组件和使用的布局管理器有关。 - Hovercraft Full Of Eels
@mooncat39:你记得像我上面做的那样将请求放在Swing事件队列中了吗?如果你还是卡住了,我认为最好的办法是创建并发布你的[mcve] -- 不过请先检查链接。 - Hovercraft Full Of Eels
2
这在Windows上按预期工作,但在Linux上不行。在Linux(Mint 17 Cinnamon)上我得到与Mooncat相同的结果,而在Windows 7上我得到与Hovercraft相同的结果。 - Alcamtar
根据要求,我更新了示例并添加了更多信息。 - mooncat39
显示剩余2条评论

1
我不认为内容面板是那样工作的。我可能错了,但我更喜欢使用自己的绘图面板(JPanel)。
我创建了一个绘图面板和一个JFrame。在创建绘图面板和JFrame之后,我将JFrame设置为NORMAL或MAXIMIZED_BOTH。这更准确地模拟了游戏从正常模式切换到最大化模式,然后再切换回正常模式。
最后,我为大小测试创建了一个单独的Runnable类。以下是来自Windows 8.1的结果。如果有人可以在Linux上运行此代码,我会很感激。
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 

主要的区别在于我使用了窗口状态监听器来检测JFrame大小的变化,并使用绘图面板的首选大小来获取正确的内容大小。
根据JFrame大小更改修改绘图面板的代码将放在windowStateChanged方法中,在调用panel.setPreferredSize方法之后。
以下是代码:
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);
                    }
                }
            });

        }
    }
}

0
如果您需要最大化的尺寸,请写入以下内容。
setExtendedState(JFrame.MAXIMIZED_BOTH);

如果您想要正常大小

setExtendedState(JFrame.NORMAL);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接