Java 通过多次调用重新绘制Swing组件的自定义绘制

Java 通过多次调用重新绘制Swing组件的自定义绘制,java,swing,repaint,Java,Swing,Repaint,在上面的代码中(完整的代码可以在“执行自定义绘制”Java教程的中找到),第一个重新绘制方法应该在前一个正方形的位置绘制一个正方形,第二个重新绘制应该在新正方形的位置绘制另一个正方形。但这实际上并没有发生。取而代之的是,前一个方块消失了,新的方块被粉刷了 前一个正方形消失时,新正方形是如何绘制的?给出了很好的解释: moveSquare方法调用重绘方法不是一次,而是两次。第一个调用告诉Swing重新绘制组件的区域,该区域以前是正方形(继承的行为使用UI委托将该区域填充为当前背景色)。第二个调用绘

在上面的代码中(完整的代码可以在“执行自定义绘制”Java教程的中找到),第一个
重新绘制
方法应该在前一个正方形的位置绘制一个正方形,第二个
重新绘制
应该在新正方形的位置绘制另一个正方形。但这实际上并没有发生。取而代之的是,前一个方块消失了,新的方块被粉刷了

前一个正方形消失时,新正方形是如何绘制的?

给出了很好的解释:
moveSquare
方法调用重绘方法不是一次,而是两次。第一个调用告诉Swing重新绘制组件的区域,该区域以前是正方形(继承的行为使用UI委托将该区域填充为当前背景色)。第二个调用绘制组件的区域,该区域当前是正方形。

您链接以回答问题的文档,至少在一般情况下:

尽管我们在同一个事件处理程序中连续调用了两次
repait
,但Swing足够聪明,可以在一次绘制操作中获取该信息并重新绘制屏幕的这些部分

当您调用
repait
时,实际上您还没有绘制任何内容,而是在将来的某个时间请求重新绘制

虽然没有详细说明,但它包含一个指向“”的链接,该链接在“”部分包含两个案例,第二个案例适用于此处:

(B) [当]绘制请求源自对
javax.swing.JComponent
扩展上的
repaint()
的调用时:

JComponent.repaint()
向组件的
RepaintManager
注册一个异步重新绘制请求,该管理器使用
invokeLater()
Runnable
排队,以便稍后在事件调度线程上处理该请求

在这一节的后面:

注意:如果在处理重绘请求之前,组件或其任何Swing祖先上发生了对
repaint()
的多个调用,那么这些多个请求可能会折叠为对
paintinstallent()
[…]的单个回叫

当事件处理程序返回时,
JPanel
的某些部分将被标记为重新绘制,可能是全部。这些区域被称为“脏区域”。Swing(最终)一次(并且仅一次)重新绘制所有脏区域。此绘制发生在您的事件处理程序返回后,即
JPanel
的外观发生更改后,因此彩色正方形显示在其新位置,在其旧位置没有任何“剩余”


简而言之,不要将
repaint
视为“立即重新绘制此区域”,而是“稍后将此区域添加到要绘制的内容列表中”。

在调用
repaint()
后,不会立即重新绘制组件。但它添加了在EDT的事件队列中再次绘制组件的请求

每个代码行中发生的情况如下所示

private void moveSquare(int x, int y) {
    int OFFSET = 1;
    if ((squareX!=x) || (squareY!=y)) {
        repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
        squareX=x;
        squareY=y;
        repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
    } 
}
标记由要重新绘制的正方形
(squareX,squareY,squareW+偏移,squareH+偏移)所限定的区域。但只有在
repaitmanager
重新绘制之前,才会重新绘制

repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
更改
squareX
squareY
的值。但它不会更改要重新绘制的早期标记区域。现在,要重新绘制的区域也是以前的值

squareX=x;
squareY=y;
标记由要重新绘制的正方形
(squareX,squareY,squareW+偏移,squareH+偏移)所限定的区域。现在,
repaitmanager
必须重新绘制两个部分。旧广场和新广场。但只有在
repaitmanager
重新绘制之前,才会重新绘制

repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
最后,当时机成熟时,
repaitmanager
绘制组件

repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
现在,该组件仅绘制了2个区域。(以前的广场和新广场)但红场将仅在新广场内绘制。在老广场上没有什么可画的。所以以前画的东西会被抹掉


实际上,尽管有两个方法调用
repaint()
paintComponents()
只调用一次。要重新绘制的总区域由
repaitmanager
处理,
paintComponents()
只调用一次。

是的,我读到了。但是这行“继承的行为使用UI委托用当前背景色填充该区域。”它说它用背景色(白色)绘制该区域但是fillRect方法应该使用当前设置的颜色绘制。为什么?调试代码您将了解在
repaint
方法中发生的情况“继承的行为使用UI委托使用当前背景颜色填充该区域。”-但是
paint
方法设置要绘制的正方形的颜色,所以这个说法实际上是不真实的。另外两个答案实际上是正确的
repait
,不会立即发生,它们会被发布到事件队列,并在将来的某个时间点进行处理,因为
moveSquare
方法是在EDT中执行的,所以可以保证在
moveSquare
方法退出之前不会发生重新绘制