I am having an odd issue when using paintComponent() and repaint().
As you see below, I have a paintComponent() class as an inner class as the main JPanel of my GUI.
// add another panel to centerInner
tableBottom = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (!paintImages.isEmpty()) {
for (PaintImages temp : paintImages) {
g.drawImage(temp.getImage(), temp.getX(), temp.getY(), this);
}
}
if (!extraCards.isEmpty()) {
for (PaintImages temp1 : extraCards) {
g.drawImage(temp1.getImage(), temp1.getX(), temp1.getY(), this);
}
}
}
};
This is a black jack game with 4 players a dealer.
repaint() is called by 4 functions:
The constructor for the initial draw.
An update method that creates an ArrayList of objects to print for the initial deal.
An another update method that creates an ArrayList for each card drawn.
And the reset which clears all ArrayLists and repaints the new initial deal.
I won't go into the backend, but every one of those four methods only run the desired number of times. just once for every time its called.
My problem is that when paintComponent is invoked by repaint(), paintComponent() runs almost 200 times, not including the for loops that run around 10 times a piece on average per game.
My question is:
1) Is this common behavior for a paintComponent method? Does paintComponent call itself repeatedly over and over again until all painting necessary has been completed?
OR
2) Does this have to do with the JPanel tableBottom? at this point nothing is actually being added to the JPanel because it is top most JPanel. But maybe paintComponent is ran repeatedly for every JPanel, JFrame, ContentPane, Label, etc.,
OR
3) Did I do something wrong in my code below? Again through testing using increments and print statements I found the update methods are called the appropriate amount of times and doing their jobs correctly.
Thanks for any help.
being a inner class I call repaint like tableBottom.paintComponent()
Never invoke the paintComponent() method directly. To repaint the panel you do:
tableBottom.repaint();
The request will be passed to the RepaintManager which will then combine repaint() requests for all components and then paint the components as necessary. This will make painting more efficient.
g.drawImage(temp1.getImage(), temp1.getX(), temp1.getY(), this);
The "this" means that images can be repainted as they are being read. That is sometimes the painting method is invoked before the image I/O has completed. So in this case when the I/O is finished another paint request will be made so the image is painted completely. If you are reading the images at the start of you class and storing them in some data structure then you can probably just use "null" instead of "this".
Did I do something wrong in my code below?
The code provide looks reasonable, but we can't see the context of how/when you invoke the painting code. I already mentioned one problem.
Post a proper SSCCE that demonstrates the problem.
Related
For my program I have a JPane that as the game progresses it adds labels to the panel, however the only way I can make the panels show up is by using add(label) then revalidating and vice versa for removing labels.
My problem is that once it gets to the point that I have more than 40 labels on the screen the revalidate has to process too many things, so how can I override the revalidate(), ether works, so that it only revalidates the specific component that was added, and not every component on the screen. I know there is a loop somewhere within the revalidate() method that will run through a loop of all components, but I just can not for the life of me find it. I would like to be able to call revalidate(component-here) or validate (component-here) and have it only update that specific component.
I know there are other ways of writing this program but I am only interested in how to override revalidate() so no "you could have redone your whole code this way which should only take you like 6 hours >.<".
http://www.fileserve.com/file/jFdQ6nv/FINAL_PROJECT.zip a link to my eclipse project, if anyone who wants to help would like to see what im actually talking abouyt
I just tried this example. Adding 1000 text areas only takes a second, and updates are instant. Labels go even faster. You might want to look at something else slowing it down.
Also, you could look at CellRendererPane. It overrides invalidate() to do nothing.
public void invalidate() {}
I'm writing an app which runs in a headless environment and needs to output to a BufferedImage instead of a screen. I have a Display class managing the BufferedImage. My app extends JPanel and in order to make it automatically repaint when a component updates, I've re-implemented repaint() as:
public void repaint(){
Graphics2D g = getDisplay().getGraphics();
paint(g);
getDisplay().repaint();
}
Whenever I start up my app, though, I get a NullPointerException when it tries to draw to the Display. This is supposedly some code in the JPanel constructor that tries to repaint. The problem is that getDisplay() returns null. However, the Display has already been instantiated and passed to the app at this point. I've verified this by having the Display print out its own properties on creation, before sending it to the app.
The exception is as follows; the topmost location refers to the line containing getDisplay():
Exception in thread "main" java.lang.NullPointerException
at com.mypapyri.clay.ui.App.repaint(App.java:28)
at javax.swing.JComponent.setFont(JComponent.java:2746)
at javax.swing.LookAndFeel.installColorsAndFont(LookAndFeel.java:208)
at javax.swing.plaf.basic.BasicPanelUI.installDefaults(BasicPanelUI.java:66)
at javax.swing.plaf.basic.BasicPanelUI.installUI(BasicPanelUI.java:56)
at javax.swing.JComponent.setUI(JComponent.java:655)
at javax.swing.JPanel.setUI(JPanel.java:153)
at javax.swing.JPanel.updateUI(JPanel.java:126)
at javax.swing.JPanel.<init>(JPanel.java:86)
at javax.swing.JPanel.<init>(JPanel.java:109)
at javax.swing.JPanel.<init>(JPanel.java:117)
at com.mypapyri.clay.ui.App.<init>(App.java:18)
at ClayOS.<init>(ClayOS.java:22)
at ClayOS.main(ClayOS.java:84)
EDIT: I've researched this and could not find a satisfactory resolution.
The repaint() method and the GUI thread
Javadocs for Component
PaintManager and RepaintManager
nidu told it first, but since he doesn't answer I put it.
Probably you're calling super() in the constructor method, and the JPanel constructor is trying to repaint before the display has been set.
i am working on an applet with around ten different datasources(e.g. statistics/error-log/...). Each datasource is updated by a single network connection and reports updates via the observer mechanism. The applet has different views which display parts of the data. Every view is only interested in some parts of the data and registers itself as an Observer at the necessary Observables.
The views(extended JPanels) mostly consist of standard swing components (e.g. JLabels, JButton, ...). Some attributes of the components in the views depend on information from the underlying data model.
Example:
StatisticPanel::paintComponent(Graphics g) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
super.paintComponent(g);
}
This logic is implemented in the paintComponent() method of the StatisticPanel and the update() methods just calls repaint(), because I didn't want the manipulate the components outside of the EDT.
Is this the intended way of updating swing components in a multi-threaded environment? Is it better to use a Runnable with SwingUtitlies.invokeLater()? Are there better approaches for this problem?
I second camickr's recommendations, but regarding this code snippet:
StatisticPanel::paintComponent(Graphics g) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
super.paintComponent(g);
}
You have non-painting methods in your paintComponent method (the first two methods), and that shouldn't be as 1) you want this method to be as lean and fast as possible and thus have only painting-related code, and 2) you do not have aboslute control of when this method is called or even if it is called, and so non-painting related code and program logic does not belong in there. For these reasons, I strongly urge you to get them out of there, but instead should be called separate from paintComponent, but as with most Swing code, on the EDT.
EDIT 1
I'm not a professional, but how about if you gave your StaticPanel a method similar to this:
public void doMyUpdate() {
if (SwingUtilities.isEventDispatchThread()) {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
clearStatisticButton.setEnabled(stat.hasEntries());
minValueLabel.setText(stat.getMinValue());
}
});
}
repaint(); // if the paintComponent method has more than just a super call.
}
EDIT 2
Also, please have a look at this thread: check-if-thread-is-edt-is-necessary
repaint() is used to invoke the Swing RepaintManger which in turn will schedule the repainting of the component, so yes it is ok to just invoke repaint directly. The RepaintManager will make sure all repainting is done on the EDT.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Java Swing revalidate() vs repaint()
Hi all
I'm fighting with my program to make it refresh at the right time.
And not having a lot of success lol
I have 2 questions
Q1: which should I use when my interface has changed: repaint or invalidate?
Q2: when should they be called? I know it sounds stupid but I'm actually having problems because of SwingWorker and other threaded operations.
Q1: which should I use when my
interface has changed: repaint or
invalidate?
If the layout is not up to date because of resizing , font change etc then you should call invalidate. Invalidating a component, invalidates the component and all parents above it are marked as needing to be laid out. Prior to painting, in the validation step if no change is found then the paint step is left out.
If there is some part of component which is being updated (defined by the graphic's clip rectangle, called "damaged" region) then you should consider calling repaint. One of the reason a damaged regions may occur is from the overlapping of a part of your component because of some other component or application.
As per my experience the repaint() is more effective if you call it on the innermost enclosing component (i.e. using public void repaint(int x, int y, int width, int height) rather than using public void repaint()).
Q2: when should they be called?
Invalidate(): marks a component as not valid -- that means, it's layout is or may not be "up to date" anymore: i.e. the component is resized, a border is added, it's font changes, etc. you should never need to call invalidate() by hand, as swing does that for you on pretty much for every property change.
When more than one region within the control needs repainting, Invalidate will cause the entire window to be repainted in a single pass, avoiding flicker caused by redundant repaints. There is no performance penalty for calling Invalidate multiple times before the control is actually repainted.
Repaint() : If the component is a lightweight component, this method causes a call to this component's paint method as soon as possible. Otherwise, this method causes a call to this component's update method as soon as possible.
Also have look at Update method.
NOTE: Swing processes "repaint" requests in a slightly different way from the AWT, although the final result for the application programmer is essentially the same -- paint() is invoked.
Refer to the link below for an excellent link on how painting is done in AWT and Swing:
http://www.oracle.com/technetwork/java/painting-140037.html
Hope this will help.
Apologies for the somewhat unclear question - couldn't think of a better way of putting it.
I use a JXTaskPane (from the Swing labs extension API) to display some information.
The user can "click" the title to expand the panel. The JXTaskPane is in a container JPanel, which is then added to a JFrame, my main application window.
I want my application window to resize to the size of the expanded task pane. To achieve this, I added a component listener to my container JPanel which would set size to the now expanded panel.
panel.addComponentListener(new ComponentListener()
{
public void componentResized(ComponentEvent e)
{
Dimension newSize = ((JXTaskPane)e.getSource()).getSize();
reSizeFrame(newSize);
}
}
private void reSizeFrame(Dimension newSize)
{
if ((newSize.height < maxSize.height) && (newSize.width < maxSize.width))
{
containerPanel.setSize(newSize);
appFrame.setSize(containerPanel.getSize());
appFrame.pack();
}
}
The problem is that the componentResized method is called as the task pane expands, as a result the resizeFrame method is called lots of times, and looks really awful on the screen.
How can I detect when the JXTaskpane has finished resizing? I thought of two approaches:
Put the resizeFrame() method in a SwingUtilities.invokeLate(..) call.
Put in a timer resizeFrame call, so any subsequent calls do not do anything until the timer fires. This should give enough time for the panel to resize.
What is the best way forward?
Also - This is my first serious Java GUI app after years of server side program. StackOverflow has been very helpful. So thanks!
I know you've already selected an answer, but overriding the paint method is definitely not correct, and while you may be able to hack something in place, it won't be ideal.
Looking at the source for JXTaskPane and specifically looking in setExpanded() (line 387), you can see it calls JXCollapsiblePane.setCollapsed(...) and then fires a property change event for expanded. A listener on that property won't be correct, because it'll fire before the animation is complete. So, if you go into JXCollapsiblePane and look at setCollapsed(...) (line 470) you'll see that if it's animated, it sets the paramaters and starts a timer. We want to know when the animation ends, so in that file, look at the animator (line 620, and specifically 652-667), which shows that when the animation ends, it fires a property change for ANIMATION_STATE_KEY with a value of "collapsed" or "expanded". This is the event you actually want. However, you don't have access to JXCollapsiblePane, so go back to JXTaskPane and search for ANIMATION_STATE_KEY, and you find line 208, which shows that JXTaskPane creates a listener on JXCollapsiblePane.ANIMATION_STATE_KEY and refires it as it's own event.
Since you do have access to JXTaskPane, you can listen for that event, so doing ...
taskPane.addPropertyChangeListener(JXCollapsiblePane.ANIMATION_STATE_KEY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
if(e.getNewValue().equals("expanded") {
...
}
else if(e.getNewValue().equals("collapsed") {
...
}
}
}
should get your event exactly when you want it.
The correct way to listen for events in Swing is through property listeners. Unfortunately, the only way to find out what the correct properties and values are is by digging through source code.
As a suggestion, have you tried overriding the paint method, first calling super and then putting your resize code at the end of that if (and only if) the size has changed significantly.
I'm not familiar with JXTaskPane, but my first reaction is that maybe you're handling the wrong event. You want the frame to resize when the user clicks on the header - so why not handle that event (perhaps using EventQueue.invokeLater() to resize the frame after the task pane has been resized)?
But if that doesn't work and you need to use the approach you've outlined above, using a javax.swing.Timer is probably best. Set it for 200 milliseconds or so and just restart() it every time componentResized() fires.