Java Swing Movement Stutter

时间:2015-04-18 08:54:08

标签: java swing

我在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);
}

}

1 个答案:

答案 0 :(得分:1)

您正在更新不属于事件派发线程的线程中的swing组件。你不应该这样做。要执行定时操作,您可以使用javax.swing.Timer

例如,不要实现Runnable,而是让您的面板实现ActionListener,删除与线程相关的内容,并添加一个动作侦听器:

public void actionPerformed(ActionEvent e) {
    update();
    repaint();
}

然后,在面板的构造函数中,您可以创建Timerjavax.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();