JFrame的getHeight()和getWidth()方法返回0。

5

我正在制作一个简单的乒乓球游戏; 碰撞机制的一部分需要获取画布的宽度和高度以重新定向球。然而,getWidth()getHeight()出现了返回0的情况。以下是主要的代码块。

package pong;

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

public class Main extends JPanel {

    static int gameSpeed = 10;
    Ball ball = new Ball(this);

    private void move() {
        ball.move();
    }

    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D g2d = (Graphics2D) g;
        ball.paint(g2d);
    }

    public static void main(String args[]) throws InterruptedException {
        JFrame frame = new JFrame("Pong");
        Main game = new Main();
        frame.add(game);
        frame.setSize(400, 400);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        while (true) {
            game.move();
            game.repaint();
            Thread.sleep(gameSpeed);
        }
    }
}

以下是实际的Ball类,处理移动条件。

package pong;

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

public class Ball extends JPanel {

    int x = 1;
    int y = 1;
    int dx = 1;
    int dy = 1;
    private Main game;

    public Ball(Main game) {
        this.game = game;
    }

    void move() {
        System.out.println(getWidth() + getHeight());

        if (x + dx < 0) {
            dx = 1;
        }
        if (y + dy < 0) {
            dy = 1;
        }
        if (x + dx > (getWidth() - 30)) {
            dx = -1;
        }
        if (y + dy > (getHeight() - 30)) {
            dy = -1;
        }
        x = x + dx;
        y = y + dy;
    }

    public void paint(Graphics2D g) {
        g.fillOval(x, y, 30, 30);
    }
} 

编辑:问题已解决,我只是没有告诉getWidth()和getHeight()要参考什么。显然,如果我不告诉它们要获取什么,它们就会返回null。更正错误的方法很简单,只需将它们更改为game.getWidth()和game.getHeight()。尽管如此,感谢您的帮助!您所有的输入也对其他领域有所帮助 :)


不要覆盖paint方法,而应该使用paintComponent方法。确保调用super.paintXxx以维护paint链。在Swing中要注意不要使用无限循环,因为它们可能会阻止应用程序被重新绘制。Swing Timer会更安全,或者使用显式线程。不要依赖魔数(一旦纠正),应使用getWidth和getHeight来确定应该绘制的区域。 - MadProgrammer
一个小例子,用于覆盖getPreferredSize(),请阅读链接中指定的内容以获取更多帮助 :-) - nIcE cOw
2个回答

8
  1. 默认情况下,Graphics/Java2D 没有返回合理的 Dimension , 结果为零的 Dimension ,您需要覆盖 JPanelgetPreferredSize 方法,然后 getWidth/Height 将返回正确的 JPanels 大小坐标。

  2. 然后使用 JFrame.pack() 而不是任何调整大小的方式。

  3. 覆盖 Swing JComponentspaintComponent 方法,而不是 paint() 方法,在 paintComponent 中,第一行代码应该是 super.paintComponent ,否则会导致累计绘制。

  4. 绝不能在 Swing 中使用 Thread.sleep(int),也不能用于 Java7 和 Swing 中的自定义绘图或动画,要使用 Swing Timer 代替无限循环停止的 Thread.sleep(int)


你能进一步解释如何覆盖 getPrefferedSize 吗? - Googly_

4

这是一个复杂的问题,由于某些设计决策而变得更加复杂。

通常情况下,Swing希望使用布局管理器内的组件。布局管理器需要一定的信息才能决定如何最佳地布置这些组件。这包括获取首选/最小/最大尺寸。

你面临的问题是你并不想使用布局管理器,因为你想手动定位Ball。当你放弃布局管理器时,你就要负责很多工作,确保组件在父容器中正确的大小和位置。

一个简单的解决方案是生成一个“游戏”界面,你可以在上面绘制游戏元素或实体。

问题是,你似乎不知道应该使用哪种方法。你的Ball扩展自JPanel,但你的Main面板正在自绘...这不是组件应该呈现的方式。

与其将组件作为游戏实体的基础,引入了许多问题和管理开销,不如直接使用Main作为主要游戏界面,并直接将所有实体渲染到其中。这给了你更多的控制,并简化了过程 - 在我看来

例如...

import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Main extends JPanel {

    public static final int GAME_SPEED = 40;
    Ball ball = new Ball();

    protected void move() {
        ball.move(this);
    }

    public Main() {
        setLayout(null);
        Timer timer = new Timer(GAME_SPEED, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                move();
                repaint();
            }
        });
        timer.start();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); 
        ball.paint((Graphics2D)g);
    }

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

    public static void main(String args[]) throws InterruptedException {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Pong");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                Main game = new Main();
                frame.add(game);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            }
        });
    }

    public interface Entity {

        public void paint(Graphics2D g2d);
        public Rectangle getBounds();

    }

    public interface MovableEntity extends Entity {

        public void move(Container parent);

    }

    public class Ball implements MovableEntity {

        private int dx = 1;
        private int dy = 1;

        private int x;
        private int y;

        @Override
        public void move(Container parent) {

            int width = parent.getWidth();
            int height = parent.getHeight();

            int x = getX();
            int y = getY();

            x += dx;
            y += dy;

            if (x + dx < 0) {
                dx = 1;
            }
            if (y + dy < 0) {
                dy = 1;
            }
            if (x + dx > (width - getBounds().width)) {
                dx = -1;
            }
            if (y + dy > (height - getBounds().height)) {
                dy = -1;
            }

            setLocation(x, y);

        }

        @Override
        public void paint(Graphics2D g2d) {
            int width = getBounds().width;
            int height = getBounds().height;
            int dim = Math.min(width, height);

            int xPos = x + ((width - dim) / 2);
            int yPos = y + ((height - dim) / 2);

            g2d.fillOval(xPos, yPos, dim, dim);
        }

        @Override
        public Rectangle getBounds() {
            return new Rectangle(x, y, 30, 30);
        }
    }
}

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