Java 当请求太快时,Swing组件(重新)绘制机制无法正确绘制
从Java的文档中可以看出,组件的重绘机制经过了优化和缓存,因此即使多次调用,也不会阻塞绘图管道 似乎在非常繁重的调用下,有时它无法绘制最新的帧。考虑下面的例子:Java 当请求太快时,Swing组件(重新)绘制机制无法正确绘制,java,swing,paint,paintcomponent,repaint,Java,Swing,Paint,Paintcomponent,Repaint,从Java的文档中可以看出,组件的重绘机制经过了优化和缓存,因此即使多次调用,也不会阻塞绘图管道 似乎在非常繁重的调用下,有时它无法绘制最新的帧。考虑下面的例子: import java.awt.Graphics; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class TestRepaint extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setContentPane(new TestRepaint());
frame.setSize(300, 100);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private long start;
{
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
start = System.currentTimeMillis();
System.out.println("Repaint");
repaint();
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
long now = System.currentTimeMillis();
int where = (int) ((now - start) / 5);
if (where >= 200)
g.drawString("DONE", 200, 30);
else {
g.drawString("Working...", where, 30);
repaint();
}
}
}
当鼠标点击窗口时,“工作…”文本将向右滚动。在最后一帧,它应该变成“完成”。它似乎(随机)无法绘制最后一帧,而之前的“工作…”文本仍然保留。例如,如果我们强制重画显示(即更改窗口大小),则显示“完成”
看起来,由于重新绘制请求太快,组件的最后一次重新绘制失败。那么,有没有什么方法可以保证做到这一点 1)永远不要从绘制方法中调用
repaint()
——永远不要。2) 您应该覆盖paintComponent,而不是paint。3) 您应该在覆盖中调用super的方法。4) 阅读教程中解释的内容。通过在paint中调用repaint()
,您将创建一个完全不可控且笨拙的动画,并可能会弄乱绘制链。如果你想要动画,用一种可以控制的方式来做,而不是有这个潜在的问题。每个方法都有一个特定的功能。绘画方法只适用于绘画。它不用于安排组件的绘制。Swing组件设计为在组件的属性更改时重新绘制自己。当您更改文本、字体、前景等时,组件将重新绘制自身。如果同时更改所有3个属性,则RepaitManager会将3个重新绘制请求合并为单个paint()请求。对于动画,您可以使用Swing定时器来安排重新绘制。为了进一步支持装满鳗鱼的气垫船和Camickr的评论,从paint
方法中调用repaint
,可以设置一个不受控制的更新过程,这将使EDT充满重新绘制请求,这将使其变得困难(或耗时)让它处理其他事件,并最终使CPU 100%运行。一个简单的解决方案是使用Swing定时器
更新动画的状态,并定期安排一次重新绘制
(例如,对于60fps为16毫秒),或者如果希望对帧速率进行更多控制,则安排一次线程
,但这会带来更多的问题issues@Panayotis老实说,不要。Swing使用一个被动渲染引擎,它经过优化以这种方式工作,因此它不是为高频更新周期而设计的。Swing还可以将多个重绘请求减少为较少的重绘事件(这就是您可能遇到的情况)。相反,您需要尝试以足够小的间隔生成更新,以实现您的目标,但要足够大,以允许EDT时间呼吸和处理其他事件