有这篇文章:
有人插队了! 时不时地,一些 Swing 事件似乎在事件队列中以错误的顺序处理(没有什么比有人插队更让我热血沸腾的了)导致奇怪的行为。这最好用一个小代码片段来说明。阅读下面的片段并仔细思考您想象的事件发生的顺序。
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
repaint();
doSomething();
}
});
大多数开发人员会认为 repaint() 方法会导致在 doSomething() 方法调用之前发生绘画操作。然而实际上并非如此,对 repaint() 的调用将创建一个新的绘制事件,该事件将被添加到事件队列的末尾。这个新的绘画事件只会在当前的 Action 事件完成后被处理(调度)。这意味着 doSomething() 方法将在调度队列上的新 Paint Event 之前执行。
这里的关键点是对 repaint() 的调用将创建一个新的绘制事件,该事件将被添加到结束事件队列中,而不是立即处理。这意味着没有事件会插队(我的血液可以保持在正确的温度)。
我的问题是,如何强制 Swing 执行 repaint();
之前 doSomething();
?
此外,如果有调用 repaint()
doSomething();
中的方法它们只会在 doSomething();
之后执行完成了。有什么办法可以暂停 doSomething();
中间执行,然后抛出 reapaint();
,完成它,然后继续 doSomething();
?
目前我找到的唯一解决方案是 this(link) , 但它不是很实用...
最佳答案
好吧,您和所引用文章的作者都没有捕获要点。 “重绘”方法调用只是通知重绘管理器:
- 有一个要重绘的组件(你称之为“重绘”)
- 它应该用 (x,y,w,h) 剪辑重新绘制(如果您调用“重新绘制”而没有指定 rect - 它将是整个组件边界,(0,0,w,h))
因此,何时重绘并不重要,因为如果您为同一组件一个接一个地调用大量重绘,您甚至可能不会注意到它。这也是为什么这些后续调用可能会合并的原因。检查这个例子:
private static int repaintCount = 0;
public static void main ( String[] args )
{
final JComponent component = new JComponent ()
{
protected void paintComponent ( Graphics g )
{
try
{
// Simulate heavy painting method (10 milliseconds is more than enough)
Thread.sleep ( 10 );
}
catch ( InterruptedException e )
{
e.printStackTrace ();
}
g.setColor ( Color.BLACK );
g.drawLine ( 0, 0, getWidth (), getHeight () );
repaintCount++;
System.out.println ( repaintCount );
}
};
component.setPreferredSize ( new Dimension ( 200, 200 ) );
JFrame frame = new JFrame ();
frame.add ( component );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setDefaultCloseOperation ( JFrame.EXIT_ON_CLOSE );
frame.setVisible ( true );
new Thread ( new Runnable ()
{
public void run ()
{
try
{
Thread.sleep ( 1000 );
}
catch ( InterruptedException e )
{
e.printStackTrace ();
}
System.out.println ( "Starting repaint calls" );
for ( int i = 0; i < 100000; i++ )
{
component.repaint ();
}
System.out.println ( "Finishing repaint calls" );
}
} ).start ();
}
这是您将看到的近似输出(可能因计算机速度、Java 版本和许多其他条件而异):
1
Starting repaint calls
2
3
4
5
6
Finishing repaint calls
7
8
“1” - 显示框架时的初始重绘。
“2,3,4...”- 由于来自单独的非 EDT 线程的调用,发生了其他七次重绘。
“但是我调用了 100000 次重绘,而不是 7 次!” - 你会说。是的,重绘管理器合并了重绘队列中相似且同时的那些。这样做是为了优化重绘并加快整个 UI。
顺便说一句,您不需要从 EDT 调用重绘,因为它不执行任何真正的绘画,只是将您的组件更新排队以备将来使用。它已经是线程安全的方法。
总而言之 - 在执行某些其他操作(这也可能导致其再次重绘)之前,应该没有真正需要重绘组件的情况。只需在需要重绘组件时调用重绘(尽可能使用指定的矩形)——重绘管理器将完成剩下的工作。除非您在 paint 方法中进行一些计算,否则这种方法效果很好,但这是完全错误的,可能会导致很多问题。
关于java - EDT队列切割,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18062126/