Java AWT 初始 drawString() 调用需要很长时间
Java AWT Initial drawString() Call Takes Long
我遇到的问题非常简单 - 我正在使用 Java AWT(和 Swing)制作游戏,但初始 Graphics.drawString(String str, int x, int y)
调用花费的时间太长。我没有设置自定义字体或任何东西。我没有设置任何渲染提示。我在 MacBook Air 中使用 Mac Sierra 10.12.4 - 我还没有在 Windows 中测试我的程序。如果这很重要的话。
如果您不想花时间阅读最小的示例,我有一个名为 GamePanel 的 class,它扩展了我在 main 方法中添加到 JFrame 的 JPanel(并实现了 runnable)。我在构造函数中初始化游戏线程,并在 run()
方法中启动游戏循环。然后使用 Swing Timers 重复游戏循环。在游戏循环中,我通过redraw()
调用paintComponent(Graphics g)
——文本是在paintComponent
方法中绘制的。这不是真正必要的信息,因为问题可以在没有游戏循环的情况下重现,只需使用游戏线程和 paintComponent
方法,但我把它放在这里只是为了以防万一。
感谢任何帮助。
游戏class (JFrame)
public class Game {
public static JFrame frame;
public static JPanel gamePanel;
public static void main(String[] args) {
frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLayout(new BorderLayout());
gamePanel = new GamePanel();
frame.add(gamePanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
游戏面板class(面板)
public class GamePanel() extends JPanel implements Runnable {
public static Thread thread;
public GamePanel() {
super();
setPreferredSize(new Dimension(640, 480));
setFocusable(true);
requestFocus();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
while (! Game.frame.isVisible()) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
EventQueue.invokeLater(new Runnable() {
public void run() {gameLoop();}
});
}
public void GameLoop() {
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
gameLoop();
}
public void paintComponent(Graphics g) {
super.paintComponent();
g.drawText("Hello World", 10, 10);
}
}
我知道最小示例中的游戏循环速度会不断变化(这与一个好的游戏循环应该做的完全相反)。但在一个最小的例子中这有什么关系呢?
发生这种情况是因为您从不同的线程调用重绘。您可以阅读有关事件调度线程的信息。
Java Event-Dispatching Thread explanation
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html
我遇到了同样的问题。我的解决方案是将我使用的字体保存在资源文件夹中,然后加载字体。
在 Eclips 中,您最容易通过 "New / Source Folder" 创建文件夹。如果您按照我的示例进行操作,文件夹的名称并不重要。我保存在资源文件夹中的字体名为 "DroidSans-Bold.ttf"
Font font = null;
try {
font = Font.createFont(Font.TRUETYPE_FONT, new File(getClass().getResource("/DroidSans-Bold.ttf").getFile()));
font = font.deriveFont(32f); // Set size to 32
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
JFrame jf = new JFrame();
jf.setSize(800, 600);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Graphics2D g = (Graphics2D)jf.getGraphics();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(font);
g.drawString("Hello", 100, 100);
我遇到的问题非常简单 - 我正在使用 Java AWT(和 Swing)制作游戏,但初始 Graphics.drawString(String str, int x, int y)
调用花费的时间太长。我没有设置自定义字体或任何东西。我没有设置任何渲染提示。我在 MacBook Air 中使用 Mac Sierra 10.12.4 - 我还没有在 Windows 中测试我的程序。如果这很重要的话。
如果您不想花时间阅读最小的示例,我有一个名为 GamePanel 的 class,它扩展了我在 main 方法中添加到 JFrame 的 JPanel(并实现了 runnable)。我在构造函数中初始化游戏线程,并在 run()
方法中启动游戏循环。然后使用 Swing Timers 重复游戏循环。在游戏循环中,我通过redraw()
调用paintComponent(Graphics g)
——文本是在paintComponent
方法中绘制的。这不是真正必要的信息,因为问题可以在没有游戏循环的情况下重现,只需使用游戏线程和 paintComponent
方法,但我把它放在这里只是为了以防万一。
感谢任何帮助。
游戏class (JFrame)
public class Game {
public static JFrame frame;
public static JPanel gamePanel;
public static void main(String[] args) {
frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLayout(new BorderLayout());
gamePanel = new GamePanel();
frame.add(gamePanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
游戏面板class(面板)
public class GamePanel() extends JPanel implements Runnable {
public static Thread thread;
public GamePanel() {
super();
setPreferredSize(new Dimension(640, 480));
setFocusable(true);
requestFocus();
if (thread == null) {
thread = new Thread(this);
thread.start();
}
}
public void run() {
while (! Game.frame.isVisible()) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
EventQueue.invokeLater(new Runnable() {
public void run() {gameLoop();}
});
}
public void GameLoop() {
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
gameLoop();
}
public void paintComponent(Graphics g) {
super.paintComponent();
g.drawText("Hello World", 10, 10);
}
}
我知道最小示例中的游戏循环速度会不断变化(这与一个好的游戏循环应该做的完全相反)。但在一个最小的例子中这有什么关系呢?
发生这种情况是因为您从不同的线程调用重绘。您可以阅读有关事件调度线程的信息。
Java Event-Dispatching Thread explanation
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html
我遇到了同样的问题。我的解决方案是将我使用的字体保存在资源文件夹中,然后加载字体。 在 Eclips 中,您最容易通过 "New / Source Folder" 创建文件夹。如果您按照我的示例进行操作,文件夹的名称并不重要。我保存在资源文件夹中的字体名为 "DroidSans-Bold.ttf"
Font font = null;
try {
font = Font.createFont(Font.TRUETYPE_FONT, new File(getClass().getResource("/DroidSans-Bold.ttf").getFile()));
font = font.deriveFont(32f); // Set size to 32
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
JFrame jf = new JFrame();
jf.setSize(800, 600);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Graphics2D g = (Graphics2D)jf.getGraphics();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(font);
g.drawString("Hello", 100, 100);