在测试使用Swingworker的一些实时仿真代码时,我注意到我的GUI似乎总是以30 fps运行,不多也不少。每次用户与应用程序交互时(如鼠标移动)或调用Swingworker的process()方法时,我都会更新GUI。 Swingworker现在没有做任何事情,它只是从GUI抓取鼠标位置并通过publish()和process()方法将其作为克隆发回(我只是这样做,看看我能做什么,能做什么在线程之间进行通信时要做,因为多线程对我来说仍然是一个新手。我在任何地方都没有定时器,Swingworker的process()方法在GUI上调用repaint(),所以我想知道是什么原因导致GUI以30 fps更新?是否可能默认情况下GUI中的vsync处于活动状态,或者它是Swingworker中process()方法的某些行为?最后:有没有办法获得更高的帧速率?
这是一个表现出这种行为的sscce:
public class SimGameTest implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new SimGameTest());
}
@Override
public void run() {
MainWindow mainWindow = new MainWindow(new Game());
mainWindow.setLocationRelativeTo(null);
mainWindow.setVisible(true);
}
}
public class MainWindow extends JFrame {
private Game game;
private GamePanel gamePanel;
public MainWindow(Game game) {
this.game = game;
createAndShowGUI();
startGame();
}
private void startGame() {
GameSim gameSim = new GameSim(game, gamePanel);
gameSim.execute();
}
private void createAndShowGUI() {
setTitle("Sim game test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
JPanel contentPane = new JPanel(new GridBagLayout());
gamePanel = new GamePanel(game);
contentPane.add(gamePanel);
add(contentPane);
pack();
}
}
public class Game {
public Point mouseLocation = new Point();
public Point clonedMouseLocation = new Point();
}
public class GameSim extends SwingWorker<Point, Point> {
private Game game;
private GamePanel gamePanel;
public GameSim(Game game, GamePanel gamePanel) {
this.game = game;
this.gamePanel = gamePanel;
}
@Override
protected Point doInBackground() throws Exception {
while (true) {
publish((Point) game.mouseLocation.clone());
}
}
@Override
protected void process(List<Point> pointList) {
game.clonedMouseLocation = pointList.get(pointList.size() - 1);
gamePanel.repaint();
}
}
public class GamePanel extends JPanel {
private Game game;
private long lastTime;
public GamePanel(Game game) {
this.game = game;
setPreferredSize(new Dimension(512, 512));
addMouseMotionListener(new GamePanelListener(););
}
@Override
public void paintComponent(Graphics g) {
// draw background
g.setColor(new Color(0, 0, 32));
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(new Color(192, 192, 255));
g.drawString(game.clonedMouseLocation.x + ", " + game.clonedMouseLocation.y, 10, 502);
long now = System.nanoTime();
long timePassed = now - lastTime;
lastTime = now;
double fps = ((double) 1000000000 / timePassed);
g.drawString("fps: " + fps, 10, 482);
}
private class GamePanelListener extends MouseInputAdapter {
@Override
public void mouseMoved(MouseEvent e) {
if (contains(e.getPoint())) {
game.mouseLocation = e.getPoint();
}
}
}
}
我创建了另一个版本,我只计算重新绘制GUI的次数并在屏幕上显示计数,它似乎以每秒30的速度增加,所以我猜fps计算不是问题
答案 0 :(得分:10)
结果SwingWorker
使用Runnable
EventQueue
将javax.swing.Timer
的实例发布到DELAY
。
private static class DoSubmitAccumulativeRunnable
extends AccumulativeRunnable<Runnable> implements ActionListener {
private final static int DELAY = (int) (1000 / 30);
...
}
您可以获得更高的帧速率,但不能使用javax.swing.SwingWorker
。您可以从source构建SwingWorker
,但最终会使用Runnable
的实例使线程饱和。另一种方法是将模型展平并定期对其进行采样,如here所示。