我在Java Swing中移动一个块时遇到了口吃。
以下代码将显示问题。跑步时,我希望盒子能够顺畅地移动而不会出现任何口吃。有没有办法在Swing中实现这一目标?或者我应该继续使用JavaFX?
MovementStutter.java
package movementstutter;
import javax.swing.JFrame;
public class MovementStutter {
public static void main(String[] args) {
JFrame win = new JFrame("Stutter Demo");
win.getContentPane().add(new GamePanel());
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setResizable(false);
win.pack();
win.setVisible(true);
}
}
GamePanel.java
package movementstutter;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class GamePanel extends JPanel implements Runnable {
private boolean running;
private Thread thread;
private double x;
private double y;
private double movementFactor;
public GamePanel() {
setPreferredSize(new Dimension(640, 480));
setFocusable(true);
grabFocus();
movementFactor = 1;
running = true;
}
@Override
public void addNotify() {
super.addNotify();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
@Override
public void run() {
long time = System.currentTimeMillis();
while (running) {
update();
repaint();
long sleep = 16 - (System.currentTimeMillis() - time);
time += 16;
if (sleep < 0) {
sleep = 16;
}
try {
Thread.sleep(sleep);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void update() {
if (x < 0) {
movementFactor = 1;
} else if (x > 608) {
movementFactor = -1;
}
x += movementFactor * 200 * 0.016;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect((int) x, (int) y, 32, 32);
}
}
答案 0 :(得分:1)
您正在更新不属于事件派发线程的线程中的swing组件。你不应该这样做。要执行定时操作,您可以使用javax.swing.Timer
。
例如,不要实现Runnable
,而是让您的面板实现ActionListener
,删除与线程相关的内容,并添加一个动作侦听器:
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
然后,在面板的构造函数中,您可以创建Timer
。 javax.swing.Timer
确保在事件分派循环中执行操作。它还简化了时序管理:
public GamePanel() {
setPreferredSize(new Dimension(640, 480));
setFocusable(true);
grabFocus();
movementFactor = 1;
running = true;
new Timer(16,this).start();
}
如果你需要在任何时候停止计时器,那么你应该将它分配给一个字段,比如说
Timer blockTimer = new Timer(16,this);
然后你就做了
blockTimer.start();
在你的构造函数中,你可以有一个按钮,其动作包括
blockTimer.stop();