For some reason, my paintComponent(Graphics g) method is being called infinitely. I can't seem to tell who is calling it, even if I dump a StackTrace in the call (it's an event that's dispatched and handled).
Are there any easy ways to find out who's triggering the event?
Update: I've found the cause and I now understand the reason. Whoever answers it correctly will get the answer to the question.
Here is the code that's causing the issue:
#Override
public void paintComponent(Graphics g)
{
myJButton.setIcon(ResourceLoader.getImageIconWithLocale(MY_BUTTON_IMAGE));
super.paintComponent(g);
}
FYI: It's a really tricky one!! It's not obvious by looking at the code. I made an assumption that was wrong.
I don't know which component this is, but setting an icon on a button from within a paint routine is a bad idea. It will definitely cause the button to be repainted. If the button is a child of your component then setting the button invalidate the component too causing an infinite loop.
Set the icon somewhere else such as where the dialog / window is set up initially.
The setIcon(ImageIcon) will revalidate and repaint itself ONLY if the ImageIcon is another instance.
When working with Locales, most people are use to the ResourceBundle, which returns Strings, which in turn are immutable. Therefore setting the text over and over doesn't matter.
However, in this case, the ResourceLoader (custom class) returned a new instance of an ImageIcon. Sure it was the same Image, but it was another instance. And if you decompile the code, you'll see that setIcon (at least for JButtons), it will repaint and revalidate if newIcon != oldIcon.
The solution was to use a HashMap in the ResourceLoader, this way it saves from loading the images more than once since most of the images are used very frequently (might as well reuse the instances if you can). Turns out that overall this quick adjustment also saved a decent amount of overall memory consumption as an added bonus.
Are you calling repaint() anywhere? Also, when a window becomes visible (uncovered or deminimized) or is resized, the "system" automatically calls the paintComponent() method for all areas of the screen that have to be redrawn.
The problem is that you are setting the icon in the paintComponent() method. You should never set a property in this method.
Swing components are smart enough to repaint themselves whenever a property changes. In this case you have the problem of the component repainting itself because the Icon changes, but you are also rereading the Icon every time the component repaints itself which is also not very efficient.
Related
What is better to use:
.isDisplayable()
or:
isShowing()
to check if JButtons are visible, but also for other things? Also what is the difference between them? At this moment they look like same thing to me, but i'm a beginner.
they are giving same results, even when button was set to Opaque(false)
Don't confuse opaqueness of a component with visibility of a component.
Opaqueness is for painting the background of the component.
The component can still be visible even if the background is transparent.
Try using setVisible(false) to see the difference between the two methods.
A component can only be showing if it is:
displayable
visible
I'm having some issues repainting a JPanel on my GUI with default values.
The code I'm using right now is below, again, I'm not used to, nor really knowledgeable about java code, so forgive me for making rookie mistakes:
private void btnResetActionPerformed(java.awt.event.ActionEvent evt) {
...
pnlWagens1 = new pnlWagens();
UpdateGUI();
}
private void UpdateGUI(){
pnlWagens1.repaint();
}
So far I've tried the above code, as well as setting the JPanel to null, repainting, inserting a new instance of the panel, repainting again.
Nothing so far has been fruitful, as in the end, I'm still stuck with the old panel (and it's values) being shown on my GUI.
Basically, I make a panel with a green background initially, make the background red, then resetting the panel to have a green background again. However in the end, after hitting Reset, it still shows the old panel with the red background.
Any insight as to what I may be doing wrong/overlooking would be greatly appreciated.
Assuming this is all the relevant code (and that UpdateGUI doesn't use add or remove with the panel reference you have there), then changing what object pnlWagens1 refers to in your local class won't change other references that still refer to the old object. The old object pnlWagens1 is still referenced by Swing in another location, from when you originally called add on some container.
What you need to do is to remove pnlWagens1 from the container, change pnlWagens1 like you are doing now, readd pnlWagens1 to the container, and call then call both revalidate() and repaint() on the container.
I have a SWT application which displays and sorts image files by renaming the file. Scruffy I know, but the sorted-by-name files are the input for a later step that I cannot change.
So far I have the UI almost working but the initial image at startup does not generate a paint event, so the Canvas is blank. If I minimally resize the window, it immediately appears correctly. I am trying (perhaps incorrectly) to generate the event explicitly using the notifyListeners() method of Canvas which needs to be passed a PaintEvent and I am stuck on how to instantiate one.
What is the correct way to force this initial event to be generated?
You don't have to explicitly fire a PaintEvent. Instead use one, or combination, of the below options:
layout() marks forces the repositioning of all composite-children.
This will become visible on next repaint, which will be done
somewhere in the future, when composite's screen-area will be redrawn
redraw() marks the widget an invalidated. On next
redraw-system-action this area will be repainted.
update() forces all outstanding redraw() requests to be completed
NOW.
So for the sake of simplicity I set up a little test code just to figure out this problem. Basically I have a JFrame and I added 'this' to it (I just extended my main class from JComponent to save time). this component fills in a red background. Then I have it sleep for 2 seconds and then type this.
f.remove(this);
thing t = new thing();
f.add(t);
f.setVisible(true);
f being my JFrame object and 'thing' is just another class extending JComponent that paints a blue background..
when I comment out setvisible() it no longer changes to blue.. I've tried using t.setVisible(true) but it seems I have to make the frame visible again, not the component
does anyone know why I have to call that... or if there is another way to change components within a single frame?
"Basically I have a JFrame and I added 'this' to it (I just extended my main class from JComponent to save time). this component fills in a red background. Then I have it sleep for 2 seconds and then type this."
Don't "sleep" your program. Instead use a java.swing.Timer to perform repeated tasks on the GUI or for animation. See more at How to Use Swing Timers. You can see a bunch of Timer examples here and here and here and here and here
Instead of trying to add and remove panels look into using a CardLayout which allows you to switch between views. It will help you avoid a lot of problems that come with with adding and removing components/containers. See more at How to Use CardLayout. Also see a simple example here.
To answer your main question, whenever you remove and add components from your frame, you need to revalidate() it. setVisible() takes care of that for you.
Side Note
Seems like a lot adding an removing background panels) just to change the background. Why not just setBackround()? You can switch colors with the use of the Timer
Calling setVisible(true) makes the frame appear onscreen. Sometimes you might see the show method used instead. The two usages are equivalent, but we use setVisible(true) for consistency's sake.
After what I learned from my previous question, I would like to use a texture to paint the text on an extended JButton while it's being pressed. The first step was setting up the button's ChangeListener and stateChanged method; I have these working and can set the foreground color in the method, so that the text will be one color while the button's pressed and another while not.
Building on this, I replaced the setForeground call with the drawString code I used for my toggleButtons. It works, but immediately after the text is drawn it's overwritten by the button being automatically repainted. I tried throwing the code in a "while (model.isPressed())" loop, but that had some pretty terrible results (system hang). How would I go about manually repainting my button, so that it's only redrawn during the stateChanged method?
It seems to me that you are going the wrong way to change the look of your button. I think it would be easier for you to create a class that would handle the look and feel of your button, instead of manually handling the drawing parameters of your button inside the button's code. Blocking the repaint() calls isn't really the way to go I believe in your case.
I would personally create my own ButtonUI implementation that would handle all the paint rules (foreground color based on button state for instance), then I would call the setUI on the button, specifying an instance of this new ButtonUI as a parameter. If you don't want to handle all the drawing stuff, you can always use your new class as a proxy to the button's already existing UI handler (through JButton's getUI() method), and make changes only where you need them (I haven't tested it myself, but I'm pretty sure it would work just fine).
Of course, this represents more coding for you, but it would localize your look and feel handling in a single class, and it would fit in Swing's way of working. There are a few resources on the web to get you started (here, here and here).