I have written a custom component that extends JPanel and overridden its paint() method. Now I can see that this method is called once per 10 milliseconds when the component is displaying. Nothing changes in the component but paint() is still called. I have several calls repaint() but none of them is called actually. How to know what is causing such frequent updates?
UPDATE!
There was "bug" in my code. I was updating inner components form paint() method so it was the root cause of continuous repainting.
But still, the question is not answered: how to understand what supplies events to the queue?
how to understand what supplies events to the queue?
Whenever a property of a Swing component is changed the component will automatically invoke repaint() on itself. The paint request is passed to the RepaintManager.
The RepaintManager will then consolidate multiple repaint requests into a single painting of all components. The consolidation is done to make painting more efficient.
So the individual component that made the request is not available because in many cases multiple components will make the repaint request at the same time.
You can read Painting in AWT and Swing for a more detailed explanation.
using debug
make a break point in the paint() function
and when it called you can watch the stack trace of the call back
Related
I have a JPanel with some JButtons on it. When the JButtons are clicked, an event handler is invoked. Within this event handler, I would like the capability of having the JPanel repaint multiple times. There is a lot of processing that occurs in this event handler over the course of several seconds, and I need to be able to update the JPanel to display incremental updates to the user. However, when I call repaint() on the JPanel within the event handler, nothing seems to happen. The JPanel waits to repaint until the event handler has returned.
I have tried using the repaint(long tm) method, but that doesn't seem to help. How do I get this desired behavior of repainting a JPanel multiple times from within an EDT?
Swing is single threaded, so event handlers and painting occur on a single thread (the EDT). If you have computation that takes time and attempt to do so on the EDT, no repainting (or anything else) can be performed. To overcome this, perform the long running tasks on a separate Thread or use a SwingWorker
As most all similar question answers will tell you -- use a SwingWorker to do the long-running task. Push updates to the GUI via the SwingWorker's publish/process method, and when the updates are passed into the GUI, repaint it. This way you avoid stomping on the Swing event thread and avoid freezing your program. Please check out Concurrency in Swing. Also have a look at my code in this answer to a similar question.
I have searched through the many questions saying wait until repaint has finished but I have never found an answer that I could really implement or understand!
I don't want the code to continue when the screen has been repainted!
I have a simple function:
public static void clearScreen()
{
panel.repaint();
}
I can tell you now that this works!
The only problem being that it does not wait to repaint so for example:
public static void blah()
{
drawSomething();
clearScreen();
drawSomething();
}
There is a good chance that both drawings would get clear from the screen. As you might be able to guess I do not want this!
I would just like to mention that all the separate functions work!
Requesting a repaint on a component will add a repaint event to the Event Dispatch Thread which is a separate background thread that will actually call paint() later.
Depending on what you are doing in drawSomething() may happen before or after the event dispatch thread has repainted. The best thing to do is to do all of your work on the event dispatch thread by doing your "draw something" inside a call to SwingUtilities.invokeLater. Which will run your code on the event dispatch thread. All processes on the event dispatch thread are executed in the order they are submitted (usually, some repaint requests are combined etc) so the logical order of calls are preserved.
Learn more about the Event Dispatch Thread in the Oracle Java Tutorial
Swing uses a passive painting system, that is, the screen is not updated on a regular bases, but is painted as is required (or more precisely, when ever the RepaintManager thinks it should be).
You can make requests that UI be updated by calling repaint, buts it's up to the RepaintManager to decide what and when that should occur. The RepaintManager will place a repaint event on the event queue which will be processed by the Event Dispatching Thread at some time in the future.
What this basically means, is that is generally not possible to know when a paint may occur (as painting may happen for all sorts of reasons).
Take a look at Painting in AWT and Swing for more details
You could...
Employee your own buffer, onto which you paint when ever you want, but is the painted to the screen independently. You can use a BufferedImage for this. The problem with this is you're going to have to monitor the changes to the container to ensure that the buffer is the right size
You could...
Implement your own queue, which you add "commands" to, which is then processed by your paint method
The problem with this is ensuring that you lock or copy the queue before processing it
I'm trying to understand when to use revalidate/repaint/pack.
Surprisingly I haven't found much detailed under-the-hood documentation (feel free to link).
So far I have understood that this is all the responsibility of the RepaintManager.
paint/repaint refer to what sees as dirty/clean
pack/validate/revalidate refer to what is valid
This article on Oracle explains that calling a repaint enqueues a job on the Event Dispatcher Thread that will in turn call paintImmediately() on the component (this redraws the component).
This trail indicates that to be or not to be valid is associated with the LayoutManager. And that this is all about the size of the component rather than the content.
Is it true that you should call revalidate when you move/resize your component and repaint when you change it's contents?
Is the pack() method really a deprecated thing that you should never call?
Are any of the above claims wrong?
Here are a few basic cases where you need to invoke those methods (I cover the basics but I may have missed a few other cases where calling those methods would be required).
You should call revalidate() on a container when you have either: added one or more component, removed one or more components, changed the constraints of one or more contained components (constraints or XXXSize(), although the latter is not recommended), changed the LayoutManager of the container.
You should call repaint() whenever you want that component (and its descendants) to be repainted. Eventually, this will call paintComponent() (for basic widgets this will delegate to XXXUI.paint()), paintBorder() and paintChildren() (at least in Swing)
pack() actually sets the size of a window to its preferred size. You should usually call this right before making the window visible. You may call it later on but this will give a weird user experience (I don't know many applications that resize their windows once displayed)
The main advantage of using revalidate() and repaint() is that they can coalesce themselves. This means that if you call several times repaint(), the painting will only be performed once.
I realize that without code this might be hard to answer but the problem is I am not sure what code to post. My problem is that when I load an XML file, unmarshal it and then iterate a ArrayList of values loaded in to repopulate a JLabel array that get placed on a JPanel inside a JScrollPane, the graphics do not update until the entire processing is done. I discovered this when I created a JFrame that opened up with a JProgressBar. The bar does not even show or paint on the pane Frame until everything is done. This defeats the purpose since I am trying to have the progress bar show the progress of the decoding. While debugging that, I discovered that if I isolated the code (except for actually loading the file and doing the unmarshalling) the bar worked. That got me looking and I noticed that the other components mentioned were not updating when the individual JLabels were added and sized until all process was complete despite constant repaints. Since I am still new to JAVA I thought I would ask if anyone might know a reason that all of these components would not be graphically updating while this loop is going on.
As a note, in the loop, I call the add and repaint functions to place the newly decoded JLabel on the pane but it does not show until all is complete just like the bar. And all of this is running single thread (except for the JProgressBar which is spun into it's own thread).ll
I can post code if you like just please direct me to which code you want (IE the XML decoder and loop, the add and size of the JLabels, the instantiations, etc).
Your problem is that you're doing all processing on the Swing event thread or EDT. The solution is to use a background thread such as can be obtained by a SwingWorker object.
For more on this, please check out the Java Swing tutorial called Concurrency in Swing. It will describe the event thread, why it is important to respect and not to block, and how to do background processing with a SwingWorker object.
You are doing your work on the "event dispatch" thread, which is the same thread used to run swing.
You need to do your work in a separate thread. Take a look at the SwingWorker class
I'm putting together a Swing application where I often want to replace the contents of a JPanel. To do this, I'm calling removeAll(), then adding my new content, then calling revalidate().
However I'm finding that the old content is still actually visible (though obscured by the the new content). If I add a call to repaint() in addition to revalidate(), it works as expected.
I'm sure on other occasions I've experienced that just calling revalidate() is enough.
So basically my question is - should I need to call both functions and if not, when should I call each of them?
You need to call repaint() and revalidate(). The former tells Swing that an area of the window is dirty (which is necessary to erase the image of the old children removed by removeAll()); the latter tells the layout manager to recalculate the layout (which is necessary when adding components). This should cause children of the panel to repaint, but may not cause the panel itself to do so (see this for the list of repaint triggers).
On a more general note: rather than reusing the original panel, I'd recommend building a new panel and swapping them at the parent.
Any time you do a remove() or a removeAll(), you should call
validate();
repaint();
after you have completed add()'ing the new components.
Calling validate() or revalidate() is mandatory when you do a remove() - see the relevant javadocs.
My own testing indicates that repaint() is also necessary. I'm not sure exactly why.
revalidate is called on a container once new components are added or old ones removed. this call is an instruction to tell the layout manager to reset based on the new component list. revalidate will trigger a call to repaint what the component thinks are 'dirty regions.' Obviously not all of the regions on your JPanel are considered dirty by the RepaintManager.
repaint is used to tell a component to repaint itself. It is often the case that you need to call this in order to cleanup conditions such as yours.
revalidate() just request to layout the container, when you experienced simply call revalidate() works, it could be caused by the updating of child components bounds triggers the repaint() when their bounds are changed during the re-layout. In the case you mentioned, only component removed and no component bounds are changed, this case no repaint() is "accidentally" triggered.
yes you need to call
repaint();
revalidate();
when you call removeAll() then you have to call repaint() and revalidate()