JPanel 宽度超过 getPreferredSize() 中定义的尺寸

JPanel width exceeding the dimension defined in getPreferredSize()

我正在尝试创建一个闯关游戏,但我发现了一个关于游戏 "scene" 面板大小的恼人问题。

这是我的Boardclass,这是游戏所有组件分布的面板:

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

class Board extends JPanel {

    private static final long serialVersionUID = 1L;
    public final int WIDTH = 400;
    public final int HEIGHT = 300;
    private final int UPDATE_INTERVAL = 20;

    private Timer timer;
    public Ball ball;
    public Paddle paddle;

    private JLabel scoreLabel, score;   

    public Board() {

        paddle = new Paddle(this);
        ball = new Ball(this);

        setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));

        scoreLabel = new JLabel("Score: ");
        scoreLabel.setFont(getFont().deriveFont(12f));
        score = new JLabel();
        score.setFont(getFont().deriveFont(12f));
        this.add(scoreLabel);
        this.add(score);

        this.addKeyListener(new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                paddle.keyPressed(e);
                if (e.getKeyCode() == KeyEvent.VK_SPACE){
                    starGame();
                }
            }

            @Override
            public void keyReleased(KeyEvent e) {
                paddle.keyReleased(e);
            }
        });

        ActionListener action = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                updateBoard();
                repaint();
            }
        };

        timer = new Timer(UPDATE_INTERVAL, action);

        setFocusable(true);

    }

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

    private void updateBoard() {
        ball.move();
        paddle.move();
        repaint();
    }

    public void gameOver() {
        JOptionPane.showMessageDialog(this, "Game Over");
        newGame();
    }

    public void starGame() {
        timer.start();
    }

    public void stop() {
        timer.stop();
    }

    public void newGame() {
        stop();
        paddle = new Paddle(this);
        ball = new Ball(this);
        repaint();
    }

    public void setSpeed(int speed) {
        ball.setSpeed(speed);
    }


    @Override
    protected void paintComponent(Graphics g) {

        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        ball.paint(g2);
        paddle.paint(g2);
    }
}

在上面的代码中,可以看出我根据固定值设置宽度和高度,并且我重写了preferredSize()方法以符合这些措施。

问题是屏幕边缘的碰撞逻辑是基于我在 JPanel 中设置的那些测量值,但由于某种原因它在右侧得到了一个额外的 space,可以在下面的 gif 中,球和桨在右角的碰撞看到了:

我认为问题可能与 有关,但额外的 space 出现在 JPanel.

在 Ball class 中,我用来检测右角边界反转方向的公式在 classes 的 move() 方法中:

import java.awt.Graphics2D;
import java.awt.Rectangle;

public class Ball {

    private int x = 0;
    private int y = 15;
    private final int DIAMETER = 30;
    private int xSpeed = 1;
    private int ySpeed = 1;

    private final Board board;
    private final Paddle paddle;


    public Ball(Board board) {
        this.board = board;
        this.paddle = board.paddle;
        y = paddle.getTopY() - DIAMETER;
        x = board.WIDTH / 2 - DIAMETER / 2;
    }

    public void move() {

        if (x >= board.WIDTH - DIAMETER || x <= 0) {
            xSpeed = -xSpeed;
        }

        if (y < 15) {
            ySpeed = -ySpeed;
        }

        if (y + DIAMETER > paddle.getTopY() + paddle.HEIGHT) {
            board.gameOver();
        }

        if (collision()) {

            float paddleCenter = paddle.getX() + (paddle.WIDTH/2);

            float relativePos = (this.x + (DIAMETER/2) - paddleCenter) / (paddle.WIDTH/2);

            if((relativePos > 0 && xSpeed < 0) || (relativePos < 0 && xSpeed > 0)){
                xSpeed = -xSpeed;
            }

            ySpeed = -ySpeed;
            y = paddle.getTopY() - DIAMETER;
        }

        x += xSpeed;
        y += ySpeed;
    }

     [...]
}

Class 桨:

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

public class Paddle {

    private int x = 0;
    private final int topY;
    public final int WIDTH = 100;
    public final int HEIGHT = 10;
    private int direction = 0;

    private Board board;

    public Paddle(Board board) {
        this.board = board;
        topY = board.HEIGHT;
        x = board.WIDTH / 2 - WIDTH / 2;

    }

    public void move() {
        if (x + direction >= 0 && x + direction <= board.WIDTH - WIDTH) {
            x = x + direction;
        }
    }

    public void paint(Graphics2D g2) {
        g2.fillRect(x, topY, WIDTH, HEIGHT);
    }

    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            direction = -5;
        }
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            direction = 5;
        }
    }

    public void keyReleased(KeyEvent e) {
        direction = 0;
    }

    public Rectangle getBounds() {
        return new Rectangle(x, topY, WIDTH, HEIGHT);
    }

    public int getTopY() {
        return topY;
    }

    public int getX() {
        return x;
    }
}

如何通过保持 JPanel 的大小固定来删除此 space?

由于问题中的可测试片段会很大,我在 Gist 中添加了一个可执行代码,其中包含所有 classes(BallPaddleBoard) 涉及。

我们不知道您的应用程序是如何构建的。例如,您是否将 Board 面板直接添加到框架中?或者您可能将 Board 面板嵌套到另一个面板中,而您看到的额外 space 来自父面板?

您是否尝试将 LineBorder 添加到看板面板。如果边界绘制正确并且球仍然没有到达边缘,那么您的碰撞逻辑有问题。

setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));

这没有意义,因为您正在对球和球拍进行自定义绘画。如果有的话,布局应该是空的。

编辑:

这看起来很可疑:

frame.pack(); 
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
frame.setLocationRelativeTo(null); 
frame.setResizable(false); 
frame.setVisible(true);

您 pack() 框架,然后更改框架的 属性。因为框架不可调整大小,所以不需要绘制边框,所以 Border 的额外宽度现在添加到面板,但您的逻辑检查硬编码值,而不是面板的实际宽度。

代码应该是:

//frame.pack(); 
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
frame.setLocationRelativeTo(null); 
frame.setResizable(false); 
frame.pack(); 
frame.setVisible(true);

请注意问题与您发布的代码无关。

这就是为什么每个问题都应该在论坛中发布适当的 MCVE,而不是在其他网站上!!!