我有一个小型引擎,在我的 OSX 笔记本电脑上运行良好,但在功能较弱的 Linux PC 上运行时会崩溃或严重滞后。我最小化了代码,使其只是一个小类,但仍然存在完全相同的滞后性。我认为这与缓冲区策略和线程有关。这是类(class):
public class Test extends Canvas implements Runnable {
private Thread thread;
private boolean running = false;
public Test()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(1000, 1000));
frame.add(this);
frame.setVisible(true);
start();
}
public static void main(String[] args)
{
new Test();
}
public synchronized void start()
{
this.thread = new Thread(this);
this.thread.start();
this.running = true;
}
public synchronized void stop()
{
try
{
this.thread.join();
this.running = false;
}
catch(Exception e)
{
e.printStackTrace();
}
}
public void run()
{
while(running)
{
render();
}
}
private void render()
{
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
this.createBufferStrategy(2);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0, 0, 1000, 1000);
g.dispose();
bs.show();
}
}
最佳答案
我看到 Canvas 被涂成黑色之前有延迟。在将 JFrame 设置为可见之前,您必须在 Canvas 上绘制一些内容。
以下是我对您的代码所做的更改。
我使用了 JPanel 而不是 Canvas。我使用 JPanel 获得自动双缓冲。
我通过调用 SwingUtilities invokeLater 方法启动了 Swing 应用程序。这将 Swing 组件的创建和执行放在 Event Dispatch thread 上。 。 Oracle 和我坚持所有 Swing 应用程序都以这种方式启动。稍后在本解释中,您将了解为什么这很重要。
我每 250 毫秒绘制一个白色图像,然后绘制一个黑色图像。这样,我就可以确定绘画代码正在工作。
我将线程代码移动到它自己的类中。我遇到了线程和 JPanel 使用 boolean 值的问题。通过将线程放入其自己的类中,我可以为线程和 JPanel 创建单独的 boolean 值。
在 JPanel PaintComponent 方法中,我在将 JFrame 设置为可见之前绘制了一些内容。
我设置了 JPanel 的首选大小,而不是 JFrame 的大小。无论如何,我想知道绘图面板的大小。
在线程代码中,对绘图 JPanel 的方法调用放置在 SwingUtilities invokeLater 方法内。这确保了绘制发生在事件调度线程上,而定时循环在不同的线程中执行。这可确保 GUI 保持响应。
这是代码。
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawingTest extends JPanel {
private static final long serialVersionUID = 2584117430541789858L;
private DrawingTestRunnable drawingTestRunnable;
private boolean isWhite;
public DrawingTest() {
this.setPreferredSize(new Dimension(1200, 700));
this.isWhite = true;
JFrame frame = new JFrame("Drawing Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
frame.setVisible(true);
start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new DrawingTest();
}
});
}
public synchronized void start() {
drawingTestRunnable = new DrawingTestRunnable(this);
new Thread(drawingTestRunnable).start();
}
public boolean isWhite() {
return isWhite;
}
public void setWhite(boolean isWhite) {
this.isWhite = isWhite;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (isWhite) {
g.setColor(Color.WHITE);
} else {
g.setColor(Color.BLACK);
}
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.dispose();
}
public class DrawingTestRunnable implements Runnable {
private boolean isWhite;
private volatile boolean running;
private DrawingTest drawingTest;
public DrawingTestRunnable(DrawingTest drawingTest) {
this.drawingTest = drawingTest;
this.running = true;
}
@Override
public void run() {
long duration = 250L;
long startTime = System.currentTimeMillis();
while (running) {
repaintPanel();
long elapsedTime = System.currentTimeMillis() - startTime;
long loopDuration = Math.max((duration - elapsedTime), 5L);
sleep(loopDuration);
startTime = System.currentTimeMillis();
}
}
private void repaintPanel() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
drawingTest.repaint();
isWhite = !isWhite;
drawingTest.setWhite(isWhite);
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
}
}
public synchronized void setRunning(boolean running) {
this.running = running;
}
}
}
关于Java 缓冲区策略导致严重滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40025009/