我有两节课。我试着移动圈子。当我在Game.java中键入break
语句时,它会为我绘制一个没有移动的圆圈。没关系。但是,当我想移动圆圈时(通过删除break
它什么也没有显示。我不知道如何调试它。我发现当我在没有break;
语句的情况下使用时,程序不会进入paint方法。<登记/>
任何人都可以告诉我如何调试这样的问题。提前致谢
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;
}
}
答案 0 :(得分:4)
你需要在Swing事件线程中循环,因为它会阻止Swing执行其绘图功能或用户交互。你基本上是踩着Swing事件线程,违反了Swing并发规则。使用Swing Timer代替为这类事物构建的简单动画。
答案 1 :(得分:4)
让我们打破代码...
程序从这里开始......
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,如Initial Threads ...
中所述接下来,构建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的东西都会阻止它除其他外,绘制UI
首先看一下Concurrency in Swing
现在,你有一个catch 22的情况,Swing是一个单线程框架并且不是线程安全的,这意味着你可以做长时间运行/阻塞进程,但你也无法从事件外部更新UI调度线程。
在您的情况下,一个简单的解决方案是将while-loop
替换为javax.swing.Timer
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();
}
请查看How to use Swing Timers了解更多详情......
作为旁注,通常建议使用paintComponent
对paint
执行自定义绘画。绘画是一系列复杂的方法调用,你可以做的任何事情都可以防止它被搞砸......
有关详细信息,请查看Painting in AWT and Swing和Performing Custom Painting
答案 2 :(得分:2)
当你离开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,您可以在主线程上运行绘图。