好吧,你和引用文章的作者在这里没有抓住重点。“重绘”方法调用只是通知重绘管理器:
- 有一个要重绘的组件(您称之为“重绘”)
- 它应该用 (x,y,w,h) 剪辑重新绘制(如果你调用“repaint”而不指定 rect -它将是整个组件边界,(0,0,w,h))
因此,何时发生重绘并不重要,因为如果您为同一个组件一一调用 A LOT of repaints,您甚至可能不会注意到它。这也是为什么此类后续调用可能会被合并的原因。检查这个例子:
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 调用 repaint,因为它不执行任何真正的绘画,只是将您的组件更新排队以备将来使用。它已经是线程安全的方法。
总而言之 - 在执行某些其他操作之前确实需要重新绘制组件(这也可能导致它再次重新绘制)时,不应该有任何情况。当您需要重绘组件时(如果可能,使用指定的矩形)只需调用重绘 - 重绘管理器将完成剩下的工作。除非您在绘制方法中进行一些计算,否则这种方法效果很好,这是完全错误的并且可能会导致很多问题。