所以我正在做这个练习,我需要创建一个程序,通过按下四个按钮之一在屏幕上移动一个小球。我已经完成了,但后来我想把初始位置放在屏幕的中心,所以我将值getWidth()/ 2分配给xCoord,将getHeight()/ 2分配给yCoord(首先我没有构造函数,然后当它不起作用时,我添加了构造函数并添加了repaint(),因此将调用paintComponent())但是当我启动程序时,球仍然在左上角。我怎样才能解决这个问题? 附:我将非常感谢对代码的任何评论。谢谢。
package movingaball;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingABall extends JFrame {
private JButton jbtLeft = new JButton("Left");
private JButton jbtRight = new JButton("Right");
private JButton jbtUp = new JButton("Up");
private JButton jbtDown = new JButton("Down");
private BallPanel ballPanel = new BallPanel();
public MovingABall () {
JPanel buttonPanel = new JPanel();
buttonPanel.add(jbtLeft);
buttonPanel.add(jbtRight);
buttonPanel.add(jbtUp);
buttonPanel.add(jbtDown);
this.add(ballPanel);
this.add(buttonPanel, BorderLayout.SOUTH);
jbtLeft.addActionListener(new ButtonListener());
jbtRight.addActionListener(new ButtonListener());
jbtUp.addActionListener(new ButtonListener());
jbtDown.addActionListener(new ButtonListener());
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
MovingABall mainWondow = new MovingABall();
mainWondow.setTitle("Moving a ball");
mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWondow.setSize(300, 200);
mainWondow.setVisible(true);
}
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent buttonPressed) {
if (buttonPressed.getSource() == jbtLeft)
ballPanel.left();
else if (buttonPressed.getSource() == jbtRight)
ballPanel.right();
else if (buttonPressed.getSource() == jbtUp)
ballPanel.up();
else if (buttonPressed.getSource() == jbtDown)
ballPanel.down();
}
}
class BallPanel extends JPanel {
private int xCoord = 10;
private int yCoord = 10;
public BallPanel() {
xCoord = getWidth()/2;
yCoord = getHeight()/2;
repaint();
}
@Override
public void setBackground(Color bg) {
super.setBackground(bg); //To change body of generated methods, choose Tools | Templates.
}
public void left() {
xCoord-=5;
repaint();
}
public void right() {
xCoord+=5;
repaint();
}
public void up() {
yCoord-=5;
repaint();
}
public void down() {
yCoord+=5;
repaint();
}
protected void paintComponent(Graphics aBall) {
super.paintComponent(aBall);
System.out.println("X" + getWidth());
aBall.drawOval(xCoord, yCoord, 10, 10);
}
}
}
答案 0 :(得分:2)
为ComponentListener
事件使用ComponentResized
和侦听器,但在组件达到最终屏幕尺寸之前,可以多次调用此事件。
使用AncestorListener
收听ancestorAdded
,但它遇到与ComponentListener
使用HierarchyListener
倾听hierarchyChanged
,但它遇到与ComponentListener
和AncestorListener
覆盖doLayout
,但这会遇到与ComponentListener
和AncestorListener
,HierarchyListener
...
我们需要的是知道组件在第一次显示时最后一次调整大小的时间。根据我的测试,我发现doLayout
的{{1}}和hierarchyChanged
是好的候选人。
现在问题出现了,因为我们只想在我们进入屏幕之前使用它们,之后我们就不在乎......
所以,我们需要做的第一件事就是将HierarchyListener
初始化为某些“无效”值......
x/yCoord
这给了我们一些线索,我们仍然需要“设置”坐标......
接下来,我们需要一些方法来设置可中断的回叫。某种方式可以在我们选择的“监听器”和我们实际更新坐标的时间之间注入一个短暂的延迟,但如果“侦听器”被触发则可以重置....
private int xCoord = -1;
private int yCoord = -1;
是一个很好的选择。我可以在后台等待一段指定的时间,如果我们需要它可以重新启动...
javax.swing.Timer
最后,当我们选择的“听众”被触发时,需要“重新启动”计时器。
为简单起见,我去了 private Timer resizeTimer;
public BallPanel() {
resizeTimer = new Timer(125, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Only update the coorinates if they are invalid...
if (xCoord < 0 && yCoord < 0) {
xCoord = getWidth() / 2;
yCoord = getHeight() / 2;
repaint();
}
}
});
resizeTimer.setRepeats(false);
....
doLayout
现在,你可能需要玩延迟,我发现减速250毫秒,但那只是我;)
答案 1 :(得分:1)
请参阅代码中的注释。
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingABall extends JFrame {
private JButton jbtLeft = new JButton("Left");
private JButton jbtRight = new JButton("Right");
private JButton jbtUp = new JButton("Up");
private JButton jbtDown = new JButton("Down");
private BallPanel ballPanel = new BallPanel();
public MovingABall () {
JPanel buttonPanel = new JPanel();
buttonPanel.add(jbtLeft);
buttonPanel.add(jbtRight);
buttonPanel.add(jbtUp);
buttonPanel.add(jbtDown);
ballPanel.setBackground(Color.RED);
this.add(ballPanel);
this.add(buttonPanel, BorderLayout.SOUTH);
jbtLeft.addActionListener(new ButtonListener());
jbtRight.addActionListener(new ButtonListener());
jbtUp.addActionListener(new ButtonListener());
jbtDown.addActionListener(new ButtonListener());
}
public static void main(String[] args) {
// Should be called on the EDT!
MovingABall mainWondow = new MovingABall();
mainWondow.setTitle("Moving a ball");
mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Don't pack here. Instead return a preferred size for the
// custom comonent end..
//mainWondow.setSize(300, 200);
// ..pack() the window.
mainWondow.pack();
mainWondow.setVisible(true);
}
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent buttonPressed) {
if (buttonPressed.getSource() == jbtLeft)
ballPanel.left();
else if (buttonPressed.getSource() == jbtRight)
ballPanel.right();
else if (buttonPressed.getSource() == jbtUp)
ballPanel.up();
else if (buttonPressed.getSource() == jbtDown)
ballPanel.down();
}
}
class BallPanel extends JPanel {
private int xCoord = -1;
private int yCoord = -1;
private Dimension preferredSize = new Dimension(300,200);
/* Harmful to our logic..
public BallPanel() {
xCoord = getWidth()/2;
yCoord = getHeight()/2;
repaint();
}
*/
/* A good compiler would remove this..
@Override
public void setBackground(Color bg) {
super.setBackground(bg);
} */
public void left() {
xCoord-=5;
repaint();
}
public void right() {
xCoord+=5;
repaint();
}
public void up() {
yCoord-=5;
repaint();
}
public void down() {
yCoord+=5;
repaint();
}
/** Suggest a size to the layout manager. */
@Override
public Dimension getPreferredSize() {
return preferredSize;
}
protected void paintComponent(Graphics aBall) {
super.paintComponent(aBall);
// This will center the ball if it is the first time painted
// OR if the x or y co-ord goes off the left/top edge.
// Further logic left to user..
if (xCoord<0 || yCoord<0) {
xCoord = getWidth()/2;
yCoord = getHeight()/2;
}
System.out.println("X" + getWidth());
aBall.drawOval(xCoord, yCoord, 10, 10);
}
}
}
答案 2 :(得分:0)
您已经走上了正确的轨道,但过早地调用了getWidth()和getHeight()方法。他们仍然会返回零,因为尚未设置大小:
在构造函数中调用GetWidth()和getHeight():
MovingABall mainWondow = new MovingABall();
mainWondow.setTitle("Moving a ball");
mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
但是尺寸设置在这里:
mainWondow.setSize(300, 200);
mainWondow.setVisible(true);
那么什么时候采取行动呢?您可以覆盖setSize()
课程中的setVisible()
或MovingABall
或听取componentShown()
Listen for when a Component is Shown for the First Time建议使用HierarchyListener,这可能是最可靠的方法,但这里有点矫枉过正。
编辑: 最简单的事情可能仍然是更简单,我刚刚意识到:将BallPanel初始化为10,10但不是150,100:)
更严重的是: 将维度添加到MainWindow和BallPanel的构造函数中,而不是在MainWindow上调用setSize,您可以毫无困难地可靠地获得一致的初始状态。