为什么JLabel不断重新粉刷?

问题描述:

我有一个项目,当它存在时,它似乎不断重新绘制,导致CPU随着它在我的任何窗口中都会尖峰化。它直接从JLabel继承,与屏幕上的其他JLabel不同,它具有红色背景和边框。我不知道为什么它会不同,不断重新绘画。这个callstack看起来像这样:为什么JLabel不断重新粉刷?

Thread [AWT-EventQueue-1] (Suspended (breakpoint at line 260 in sItem)) 
    sItem.paint(Graphics) line: 260 
    sItem(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5124 
    RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1475 
    RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1406 
    RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1220 
    sItem(JComponent)._paintImmediately(int, int, int, int) line: 5072 
    sItem(JComponent).paintImmediately(int, int, int, int) line: 4882 
    RepaintManager.paintDirtyRegions(Map<Component,Rectangle>) line: 803  
    RepaintManager.paintDirtyRegions() line: 714  
    RepaintManager.seqPaintDirtyRegions() line: 694 [local variables unavailable] 
    SystemEventQueueUtilities$ComponentWorkRequest.run() line: 128 
    InvocationEvent.dispatch() line: 209  
    summitEventQueue(EventQueue).dispatchEvent(AWTEvent) line: 597 
    summitEventQueue(SummitHackableEventQueue).dispatchEvent(AWTEvent) line: 26 
    summitEventQueue.dispatchEvent(AWTEvent) line: 62 
    EventDispatchThread.pumpOneEventForFilters(int) line: 269 
    EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 184  
    EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 174 
    EventDispatchThread.pumpEvents(int, Conditional) line: 169 
    EventDispatchThread.pumpEvents(Conditional) line: 161 
    EventDispatchThread.run() line: 122 [local variables unavailable] 

它基本上只是不断地击中一遍又一遍,只要我可以按继续。这是“独一无二”的,以该特定标签的代码看起来大约是这样的:

bgColor = OurColors.clrWindowTextAlert; 
textColor = Color.white; 
setBackground(bgColor); 
setOpaque(true); 
setSize(150, getHeight()); 
Border border_warning = BorderFactory.createCompoundBorder(
     BorderFactory.createMatteBorder(1, 1, 1, 1, OurColors.clrXBoxBorder), 
     Global.border_left_margin); 
setBorder(border_warning); 

显然,它做更多,但特定块只存在这些标签所造成的秒杀/连续重绘。

任何想法为什么它会继续重新绘制这个特定的标签?

大量的代码丢失了,但我会采取一个有教养的猜测。

该类的“唯一”部分可能在负责呈现标签的代码的一部分内。如果这是真的,那么调用所有这些setXXX()方法可能会使对象变脏,这意味着它需要重新绘制,这将再次输入此代码块,然后使用公共接口更新小部件,然后使对象变脏,导致循环重复。

最终这样的事情会消耗​​所有的空闲周期,导致CPU显示最大的标签,这显然没有太大的作用。

尝试在渲染循环之外的位置设置适当的值。大多数这些项目看起来像可以在构造函数中设置。

---确认后,编辑认为它是setBorder(...)---

可能设置一个新的边界触发小部件的边框的重新计算,作为一个边界可能是更大或更小比以前的边界。这和新的边界可能包含不同的屏幕显示(提高,降低等),旧的。

这些项目都不需要在渲染部分设置,但我敢打赌,与其他项目,进行peliminary检查,看是否新项目equals(...)旧项目。如果是这样,那么(作为优化),脏位不会被设置,并且刷新请求不会被提交给渲染引擎。

有了边框,这样的检查将不得不覆盖几个元素,包括一些被编译的字节码(实际的绘图指令)。由于在考虑边界时不再是检查平等的简单优化,它们不会尝试检查平等,只是标记小部件进行重新绘制。

+0

伟大的想法,但涉及的逻辑要求他们不幸在渲染循环中设置。有各种各样的逻辑可以使事物的表现略有不同。尽管在渲染循环中创建边框似乎是问题,但您确实处于正确的轨道上(请参阅我的答案)。不确定为什么。 – Morinar 2010-05-07 20:42:33

+1

设置这些项目的逻辑不需要在paint()方法中,将它们设置在paint方法之外将导致调用paint方法。考虑一下,当绘制东西时,你不会改变自己想要绘制的东西,因为那样你就需要重绘它。如果必须更改此标签,则应在绘制标签之前完成该标签,然后该更改应该(正确)触发重新绘制的需要。 – 2010-05-07 20:53:20

+0

我相信你是对的,但是这个代码已经在生产了10年了,而且我不打算在事情上施加压力。感谢您的边界信息。 – Morinar 2010-05-07 21:48:11

这是边界。在每个涂料循环过程中创建边界,使其不断重新绘制。如果我仅仅创建边界作为类作用域的私有对象并将其设置在绘制循环中,它将正确设置边框并且不会持续重绘。如果有人知道为什么会有所作为,我会很感激这个信息。请随时对此发表评论,我会在我可以接受我的答案或者添加一个新的答案,并且我会接受你的答案。

请参阅JComponent.setBorder()中的代码。它使用简单的比较来比较新旧边界 - 它们总是会返回假,因为它们是两个不同的对象。如果条件成立,组件将被重新粉刷。因此无限循环。

作为一般规则 - 做而不是在其绘图方法中调用组件上的任何setter。就像你看到的那样,设置边框会导致无限重绘循环。这可能会发生在任何其他设置下,如果不在此版本的VM中,可能在下一个设置中。做到这一点的正确方法是在模型更改时更改组件(视图)的属性,并让Swing确定它何时将被重新绘制 - 或者自己调用repaint()。

+0

很酷,看到摇摆大师来到这里。欢迎&thx帮助! – 2010-05-08 08:20:53