【问题标题】:JFrame too small and pack() doesn't seem to workJFrame 太小并且 pack() 似乎不起作用
【发布时间】:2018-09-30 00:40:21
【问题描述】:

由于某种原因,运行时 JFrame 太小,使用 pack(); 似乎无法解决问题。有什么想法吗?

   public class Window {

    public Window(String title, Game game) {

        // Creates new JFrame
        JFrame frame = new JFrame(title);

        // Adds Game to window
        frame.add(game);
        frame.pack();   

        // Settings of Window
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

}

青色背景应该是底部和右侧的 64 x 64 边框。绘制的框是正确的,我通过调整窗口大小进行了检查。但是窗口太小,不适合画布。

游戏类:

public class Game extends Canvas implements Runnable {

private Handler handler;

public Game() {

    // Create new window
    Window window = new Window("Horses Aren't Real", this);

    handler = new Handler();
    this.addKeyListener(new KeyInput(handler));

    handler.addObject(new Daanish(100, 100, ID.Player, handler));

}

public static void main (String args[]) {

    // Creates new game
    new Game();

}

public void draw() {

    // Creates a new BufferStrategy
    BufferStrategy bs = this.getBufferStrategy();

    if (bs == null) {

        // This allows the game to preload 3 frames in order to prevent choppy framrate
        this.createBufferStrategy(3);

        return;

    }

    // Create Graphics
    Graphics g = bs.getDrawGraphics();

    g.setColor(Color.cyan);
    g.fillRect(0, 0, 1024, 640);

    g.setColor(Color.black);
    g.fillRect(0, 0, 960, 576);

    handler.draw(g);

    // Remove frame from queue
    g.dispose();
    bs.show();

}

@Override
public Dimension getPreferredSize() {

    return new Dimension(1024, 640);

}

}

【问题讨论】:

标签: java swing canvas jframe


【解决方案1】:

因此,您遇到了许多问题。首先,窗口的大小也必须考虑框架装饰,这意味着内容的可用空间是窗口的大小减去框架装饰的大小。

直接设置窗口大小是不明智的。

pack 向框架的内容询问它的首选大小,并使用它来将窗口包裹在它周围。所以,你有content size + decorations = window size,而不是window size - decorations = content size

来自JavaDocs

调整此窗口的大小以适合其子组件的首选大小和布局。如果任一尺寸小于先前调用 setMinimumSize 方法指定的最小尺寸,则生成的窗口宽度和高度会自动放大。

因此,改为覆盖Game 类的preferredSize 并返回您想要的空间量,可能类似于...

public class Game extends JPanel {
    //...
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(800, 600);
    }
    //...
}

您的下一个问题是,Window 扩展自 JFrame,但在构造函数中,您正在创建另一个 Frame ... 那么到底哪个在做什么?

消除混乱。避免从像 JFrame 这样的顶级容器扩展,你不会向它添加任何新功能

public class Window {

    public Window(String title, Game game) {

        // Creates new JFrame
        JFrame frame = new JFrame(title);

        // Adds Game to window
        frame.add(game);
        frame.pack();

        // Settings of Window
        frame.setResizable(false);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

    }

}

更新...

所以,我采用了您的“更新”代码,对其进行了修改,使其能够运行、运行并且没有任何问题......

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                Game game = new Game();
                Window window = new Window("Help", game);

                game.start();
            }
        });
    }

    public class Window {

        public Window(String title, Game game) {

            // Creates new JFrame
            JFrame frame = new JFrame(title);

            // Adds Game to window
            frame.add(game);
            frame.setResizable(false);
            frame.pack();

            // Settings of Window
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);

        }

    }

    public class Game extends Canvas { //implements Runnable {

//      private Handler handler;

        public Game() {
//
//          handler = new Handler();
//          this.addKeyListener(new KeyInput(handler));
//
//          handler.addObject(new Daanish(100, 100, ID.Player, handler));

        }

        public void start() {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        draw();
                        try {
                            Thread.sleep(40);
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            });
            thread.start();
        }

        public void draw() {

            // Creates a new BufferStrategy
            BufferStrategy bs = this.getBufferStrategy();

            if (bs == null) {
                // This allows the game to preload 3 frames in order to prevent choppy framrate
                this.createBufferStrategy(3);
                return;
            }

            // Create Graphics
            Graphics g = bs.getDrawGraphics();

            g.setColor(Color.cyan);
            g.fillRect(0, 0, 1024, 640);

            g.setColor(Color.black);
            g.fillRect(0, 0, 960, 576);

//          handler.draw(g);

            // Remove frame from queue
            g.dispose();
            bs.show();

        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(1024, 640);
        }

    }

}

但是,我在 MacOS 上运行。 setResizable 有一个鲜为人知的问题,我在 Windows 上遇到过,当调用 setResizable 时,窗口装饰会改变大小。

“基本”解决方案是在调用pack 之前调用setResziable,以便在API 决定如何更新窗口大小之前修改框架装饰。

我原以为这在 Java 的更高版本 (8+) 中已修复,但由于我不运行 Windows,因此无法测试

有关更多详细信息,请参阅My JFrame always becomes a few pixels too big.

【讨论】:

  • 如果我使用您建议的代码,窗口现在比设置的尺寸略大。这是为什么呢?
  • @FudgeMuffins "使此窗口的大小适合其子组件的首选大小和布局" - "pack 询问框架的内容是否为首选大小并使用它来包裹窗口。因此,不是窗口大小 - 装饰 = 内容大小,而是内容大小 + 装饰 = 窗口大小“
  • 是的,但是最大的尺寸是(1024, 640). 没有办法让尺寸完全达到那个尺寸吗?
  • @FudgeMuffins 不可靠。正如 AndrewThompson 所指出的,不同的平台有不同大小的装饰。你的应用程序不应该关心窗口有多大,只关心它有多大的可用区域。还要考虑在 Windows 系统上(尤其是),可以更改标题栏的字体大小(例如),这反过来又会改变可用的内容大小或窗口大小,具体取决于您使用的方法。最好放弃它,只关注内容的大小
【解决方案2】:
public class Window extends JFrame {

public Window(final int width, final int height, String title, Game game) {
    super(title);
    // Set dimensions
    Dimension d = new Dimension(width, height);
    game.setPreferredSize(d);

    // Adds Game to window
    add(game);
    // Settings of Window
    setResizable(false);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    pack();
    setVisible(true);

}

}

当你使用 pack 时,框架通常会根据内容大小调整大小

【讨论】:

  • 使用了您提出的代码,结果相同。窗口还是太小了。
  • 新版本有效,但您能解释一下为什么添加game.setPreferredSize(d); 有效吗?
  • 当你使用 pack 时,框架通常会调整为内容大小(这里是游戏)。你可以看到框架就像一个摇摆的包(不知道你是否明白我的意思^^')
  • @Mead 熟能生巧——即使在“个人”项目中,你也应该努力遵循良好的实践,它可以防止你在重要的时候变得懒惰,而且由于 OP 显然缺乏 API 的经验,它建议展示良好的做法......否则我们很快就会再次收到他们的来信,我们将不得不解释他们之前得到了不好的建议
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-14
  • 2023-03-07
  • 2016-11-29
相关资源
最近更新 更多