使用 EventDispatchThread 与不使用 EventDispatchThread 显示图像
Display an image using EventDispatchThread vs without
所以我正在尝试显示一张图像 (ball
),我最终会通过用户输入来控制它。要知道,图像只是使用线程的睡眠方法按时间间隔显示。
我制作了 2 个 classes,一个扩展了 JPanel
,另一个扩展了 JFrame
。
JPanel
subclass 看起来像这样:
public class BallPanel extends JPanel {
private Image ball;
private int x,y;
public BallPanel(){
try {
ball=ImageIO.read(new File("C:\Users\Owner\Desktop\ball.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x=10;
y=10;
Thread thread = new Thread() {
@Override
public void run(){
loop();
}
};
thread.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(ball,x,y,null);
}
public void loop(){
while(true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
在循环方法中,我调用 sleep
方法以允许 repaint
在时间间隔内被调用。然后,在构造函数中调用loop()
。
JFrame
subclass 看起来像这样:
public class BallFrame extends JFrame {
public BallFrame(){
setVisible(true);
setSize(800,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setContentPane(new BallPanel());
}
public static void main(String args[]){
//SwingUtilities.invokeLater(new Runnable() {
// @Override
// public void run() {
new BallFrame();
// }
//});
}
}
现在有趣的,或者可能是令人困惑的事情是,当我 运行 这里显示的代码,匿名内部 class 被注释掉时,球并不总是出现.有时我需要在显示球之前重新调整框架的大小(即调用 repaint
)。但是,当我使用匿名内部 class 通过偶数调度线程调用它时,每次我 运行 代码时都会出现球。这是什么原因?
这与是否从 EDT 中启动 UI 没有什么关系(尽管你应该导致这可能会导致许多其他奇怪和有趣的问题),更多的是与你的事实有关在建立 UI.
的内容之前,您已经调用了 setVisible
这可能是系统尝试启动 EDT 和 运行 以及 OS 调用在其建立之前响应之间的竞争条件示例。
无论哪种情况,您都应该从 EDT 中启动 UI,最后调用 setVisible
。
Swing 可以偷懒更新UI,这实际上是一种深思熟虑的设计选择,也是一个好主意。您并不总是希望 UI 在每次更改后更新(例如 adding/removing 组件),因此它会将一些控制权移交给开发人员以决定何时最好 revalidate
容器层次结构和请求 repaint
s
我也会避免使用 Thread
来更新 UI 的状态,因为这可能会导致油漆变脏,因为 Swing 使用被动渲染方法(在感觉需要时进行绘制)并考虑使用从 EDT 中更新的 Swing Timer
或使用 BufferStrategy
并采用主动渲染方法,然后您可以控制
所以我正在尝试显示一张图像 (ball
),我最终会通过用户输入来控制它。要知道,图像只是使用线程的睡眠方法按时间间隔显示。
我制作了 2 个 classes,一个扩展了 JPanel
,另一个扩展了 JFrame
。
JPanel
subclass 看起来像这样:
public class BallPanel extends JPanel {
private Image ball;
private int x,y;
public BallPanel(){
try {
ball=ImageIO.read(new File("C:\Users\Owner\Desktop\ball.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x=10;
y=10;
Thread thread = new Thread() {
@Override
public void run(){
loop();
}
};
thread.start();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(ball,x,y,null);
}
public void loop(){
while(true){
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
在循环方法中,我调用 sleep
方法以允许 repaint
在时间间隔内被调用。然后,在构造函数中调用loop()
。
JFrame
subclass 看起来像这样:
public class BallFrame extends JFrame {
public BallFrame(){
setVisible(true);
setSize(800,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setContentPane(new BallPanel());
}
public static void main(String args[]){
//SwingUtilities.invokeLater(new Runnable() {
// @Override
// public void run() {
new BallFrame();
// }
//});
}
}
现在有趣的,或者可能是令人困惑的事情是,当我 运行 这里显示的代码,匿名内部 class 被注释掉时,球并不总是出现.有时我需要在显示球之前重新调整框架的大小(即调用 repaint
)。但是,当我使用匿名内部 class 通过偶数调度线程调用它时,每次我 运行 代码时都会出现球。这是什么原因?
这与是否从 EDT 中启动 UI 没有什么关系(尽管你应该导致这可能会导致许多其他奇怪和有趣的问题),更多的是与你的事实有关在建立 UI.
的内容之前,您已经调用了setVisible
这可能是系统尝试启动 EDT 和 运行 以及 OS 调用在其建立之前响应之间的竞争条件示例。
无论哪种情况,您都应该从 EDT 中启动 UI,最后调用 setVisible
。
Swing 可以偷懒更新UI,这实际上是一种深思熟虑的设计选择,也是一个好主意。您并不总是希望 UI 在每次更改后更新(例如 adding/removing 组件),因此它会将一些控制权移交给开发人员以决定何时最好 revalidate
容器层次结构和请求 repaint
s
我也会避免使用 Thread
来更新 UI 的状态,因为这可能会导致油漆变脏,因为 Swing 使用被动渲染方法(在感觉需要时进行绘制)并考虑使用从 EDT 中更新的 Swing Timer
或使用 BufferStrategy
并采用主动渲染方法,然后您可以控制