Morinar
Morinar

Reputation: 3550

Why is this JLabel continuously repainting?

I've got an item that appears to continuously repaint when it exists, causing the CPU to spike whenever it is in any of my windows. It directly inherits from a JLabel, and unlike the other JLabels on the screen, it has a red background and a border. I have NO idea why it would be different enough to continuously repaint. The callstack looks like this:

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]   

It basically just continually hits that over and over again as fast as I can press continue. The code that is "unique" to this particular label looks approximately like this:

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);

It obviously does more, but that particular block only exists for these labels that are causing the spike/continuous repaint.

Any ideas why it would keep repainting this particular label?

Upvotes: 1

Views: 911

Answers (3)

Kirill
Kirill

Reputation: 425

See the code in JComponent.setBorder(). It compares the old and new border using simple comparison - which will always return false since these are two distinct objects. If the condition holds, the component will be repainted. Hence the infinite loop.

As a general rule - do not call any setters on the component in its paint methods. Just as you saw, setting the border will lead to an infinite repaint loop. This can happen with any other setting, and if not in this version of VM, maybe in the next one. The right way to do this is to change the properties of the component (view) when the model changes and let Swing figure out when it will be repainted - or call repaint() yourself.

Upvotes: 2

Edwin Buck
Edwin Buck

Reputation: 70929

Lots of the code is missing, but I'll take an educated guess.

The "unique" part of the class is probably within a section of the code responsible for rendering the Label. If this is true, then calling all these setXXX() methods probably render the object dirty, which means it needs to be repainted, which will enter this block of code again, which will then use the public interfaces updating the widget, which will then render the object dirty, causing the cycle to repeat.

Eventually such a thing will consume all spare cycles causing the CPU to max for a label that's obviously not doing much.

Try setting the appropriate values in a place which is outside the render loop. Most of these items look like they could be set in a constructor.

--- Edit after confirmation that it's the setBorder(...) ---

Setting a new border likely triggers a recalculation of the bounding box of the widget, as a border might be larger or smaller than the previous border. That and the new border might contain a different screen presentation (raised, lowered, etc) that the old one.

None of these items need to be set in the rendering section, but I'll bet that with the other items, a peliminary check is made to see if the new item equals(...) the old item. If so, then (as an optimization) the dirty bits aren't set and the request to refresh isn't made to the rendering engine.

With a border, such a check would have to cover several elements, including some which are compiled bytecode (the actual drawing instructions). Since it's no longer a simple optimization to check equality when considering borders, odds are they don't attempt to check for equality at all and just flag the widget for repainting.

Upvotes: 4

Morinar
Morinar

Reputation: 3550

It's the border. Creating the border during every paint loop some how makes it continuously repaint. If I merely create the border once as a class scoped private object and set it in the paint loop, it sets the border correctly and doesn't continuously repaint. If anybody knows why that would make a difference, I'd appreciate that info as well. Feel free to comment on this and I'll accept my answer when I can or add a new answer with tons of verbosity and I'll accept yours.

Upvotes: 0

Related Questions