[英]Why does this while loop prevent paint method from working correctly?
[英]paint method not working inside while loop in swing
我有两节课。 我尝试移动圈子。 当我在Game.java中键入break
语句时,它会为我画一个不动的圆圈。 没关系。 然而,当我想移动圆(删除break
它显示什么,我不知道如何调试,我发现了程序不会paint方法里面,当我使用,而不用。 break;
语句。
谁能告诉我如何调试这样的问题。 提前致谢
App.java
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class App extends JFrame {
App() {
JFrame jf = new JFrame();
JPanel jp = new JPanel();
jf.add(jp);
jp.setLayout(new BorderLayout());
jf.setSize(500, 500);
jf.setTitle("Chain Reactor Game");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Game game = new Game();
jp.add(game, BorderLayout.CENTER);
MenuBar menuBar = new MenuBar();
jf.setJMenuBar(menuBar.createMenuBar());
}
public static void main(String[] args) {
/*
* oracle recommendation to run swing applications in special thread
* they suggest it during thread implementation
*/
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new App();
}
});
}
}
Game.java
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class Game extends JPanel {
private static final long serialVersionUID = 1L;
int x = 0;
int y = 0;
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
System.out.println("im goint to filloval now");
g2d.fillOval(x, y, 30, 30);
}
public Game() {
while(true) {
moveBall();
repaint();
try {
System.out.println("inside thread");
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
break; // if i delete this line it not drawing. Why is that ?
}
}
private void moveBall() {
x = x + 1;
y = y + 1;
}
}
您需要从Swing事件线程中获得while循环,因为它绑住了线程,从而阻止了Swing执行其绘图功能或用户交互。 您基本上是在踩Swing事件线程,这违反了Swing并发规则。 使用Swing Timer代替为此类事情构建的简单动画。
让我们分解代码...
程序从这里开始...
public static void main(String[] args) {
/*
* oracle recommendation to run swing applications in special thread
* they suggest it during thread implementation
*/
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new App();
}
});
}
很好,它可以在“事件调度线程”的上下文中启动GUI,如初始线程中所述 ...
接下来,构建App
...
App() {
JFrame jf = new JFrame();
JPanel jp = new JPanel();
jf.add(jp);
jp.setLayout(new BorderLayout());
jf.setSize(500, 500);
jf.setTitle("Chain Reactor Game");
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Game game = new Game();
jp.add(game, BorderLayout.CENTER);
MenuBar menuBar = new MenuBar();
jf.setJMenuBar(menuBar.createMenuBar());
}
好的,这个“似乎”可以,但是如果我们仔细看一下Game
public Game() {
while(true) {
moveBall();
repaint();
try {
System.out.println("inside thread");
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
break; // if i delete this line it not drawing. Why is that ?
}
}
嗯,这是一个问题... while
是在事件调度线程的上下文中执行的,但EDT用于处理事件并帮助安排重绘,任何阻止EDT的事情都将阻止它进行绘画等用户界面
现在,这里有一个问题22,Swing是一个单线程框架,并且不是线程安全的,这意味着您可以执行长时间运行/阻止过程,但是您也无法从事件调度线程之外更新UI。
一种简单的解决方案是用javax.swing.Timer
替换while-loop
public Game() {
javax.swing.Timer timer = new javax.swing.Timer(2000, new javax.swing.ActionListener() {
public void actionPerformed(javax.swing.ActionEvent evt) {
moveBall();
repaint();
}
});
timer.start();
}
看看如何使用Swing计时器了解更多详细信息...
附带说明一下,通常建议使用paintComponent
在paint
上执行自定义paint
。 绘画是一系列复杂的方法调用,您可以做的任何防止将其拧紧的方法都可以使头发固定在位。
当您离开break;
在你有一个无限循环。 它永远不会结束,这意味着Game game = new Game();
将永远不会返回,这意味着将Game
添加到JPanel
的下一行将永远不会运行,这就是屏幕上什么都没画的原因。 为了使它起作用,您需要创建GUI,然后执行绘图。
使它起作用的一种方法是创建一个新的Thread
来执行连续绘制:
public Game() {
(new Thread() {
public void run() {
draw();
}
).start();
}
private void draw() {
while(true) {
moveBall();
repaint();
try {
System.out.println("inside thread");
Thread.sleep(2000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
或者,由于要在Swing线程上设置GUI,因此可以在主线程上运行图形。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.