JPanel 宽度超过 getPreferredSize() 中定义的尺寸
JPanel width exceeding the dimension defined in getPreferredSize()
我正在尝试创建一个闯关游戏,但我发现了一个关于游戏 "scene" 面板大小的恼人问题。
这是我的Board
class,这是游戏所有组件分布的面板:
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(Ball
、Paddle
和Board
) 涉及。
我们不知道您的应用程序是如何构建的。例如,您是否将 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
,而不是在其他网站上!!!
我正在尝试创建一个闯关游戏,但我发现了一个关于游戏 "scene" 面板大小的恼人问题。
这是我的Board
class,这是游戏所有组件分布的面板:
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 中,球和桨在右角的碰撞看到了:
我认为问题可能与 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(Ball
、Paddle
和Board
) 涉及。
我们不知道您的应用程序是如何构建的。例如,您是否将 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
,而不是在其他网站上!!!