Why is paintComponent() continuously and asynchronously being called without explicit repaint() call? - java

So this question has two parts, which I think may be related, and it's mostly abstract. Briefly, here's what I'm doing:
I have a JFrame with a JPanel and some child JPanels each with 3 JButtons on it. I also created a JComponent called glassPanel for the JFrame (i.e. myJFrame.setGlassPane(glassPanel)), that allows me to paint over the JPanels and buttons.
(1) Essentially triggered by clicking all 3 buttons on a JPanel, glassPanel is set to Visible (which appears to then call paintComponent()). This is relates to the first question.
(2) In paintComponent() I draw and paint rectangles and images, using a double buffer, onto glassPanel. This is relates to the second question.
Here's my relevant GlassPanel class code (this is not an SSCCE because it is an abstract question for now):
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;
public class GlassPanel extends JComponent {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
setDoubleBuffered(true);
Graphics2D g2 = (Graphics2D) g;
g2.drawRect(x,y,width,height);
g2.fillRect(x,y,width,height);
g2.drawImage(img, x, y, this);
}
}
By placing a System.out.print statement inside the paintComponent() method, I could tell it was being called continuously and also asynchronously. For how I think the call is made, see (1). Also, let's say I'm absolutely certain there is no call to repaint() anywhere in the code (I've checked many, many times). This is the basis of the first question.
The first time I click 3 buttons, all goes smoothly. The rectangle and images are both drawn immediately. However, when I click the next 3 buttons (at this point, glassPanel has already been setVisible(true) and the first rectangle and image are still on the screen, painted over the first 3 buttons), the second rectangle and image only load partially. When I click away from the JFrame and onto the Eclipse window that I've run the program from, the number of calls to paintComponent() rapidly increases by the same amount each time AND the partially loaded image(s) and rectangle(s) immediately and completely show up in the background JFrame. When I click back to the JFrame, the number of calls goes up again by an exact amount). This is the basis of the second question.
UPDATE: Here's something I read:
Also, when the GUI is covered by another window and then becomes
uncovered, the painting system invokes the paintComponent method with
the painting area equal to the newly uncovered area.
My questions are:
(1) why might paintComponent() be called so much without a repaint()? Or, a similar question, what might be calling paintComponent()?
UPDATE: After doing a little math, I strongly believe that it's being called by every component (all buttons and panels) continuously. But still, no call to repaint()...
(2) Why are the images loaded partially until i take focus from the JFrame window?
Note that I have tried many things: (a) creating my own doubleBuffer and using no double buffer (I know it's mostly for animation), (b) overriding and not overriding paintComponent(), (c) drawing and not drawing the image (the rectangle still takes time to load), (d) making absolutely sure there was no repaint(), (e) using and not using SwingUtilities.invokeLater(new Runnable() {
public void run() { //stuff});, (f) done an if statement to only setVisible(true) once.
I can try and past the SSCCE if I must, but I do think these are more abstract. Thanks!

Well, I think I've answered both questions. For the first, why paintComponent() was being called continuously, was that it was not actually being called continuously. It was being called by all the components when it first shows the GUI. When the Eclipse window covers it and then uncovers it, it gets called more times.
For the second, it has to do with the clipBounds of the Graphics2D object/thing. I found out how the clipBounds were changing for each paint call, so when I set the clip at the start of the paintComponent() method, the images show up immediately. (BTW, It looks GREAT!).
With a twist: after an image is displayed, each click of a button does something to the image. I haven't figured out what exactly, though. It almost looks like its repainting the same image over the old images.
So I have to figure out how to keep the old images but draw new ones when appropriate and only draw/add the new ones onto the glassPanel.
UPDATE: Calling repaint() immediately after each button is clicked helps a little. But it still causes the image to flicker somewhat, as if adding another layer, as the button is pressed, and then it returns to normal when the user lets go.

Related

Java - Repaint specific components

I'm learning Java as part of my degree but its very brief but what ever I do I like to make sure I atleast have some understanding.
Until now anything that I want displayed on the screen is put into the paintcomponent method of the JPanel.
However, I have drawn out some parts of my layout which never change, and only a certain thing in the middle rotates.
I have a timer which calls repaint().
If I correct in thinking that everything including the components that never change are being removed and then redrawn and the whole paintcomponent method is being run every time.
To me I feel like I should (or there must be) a way where the static stuff is moved out / is only drawn once, and only the parts that I specifically want to be redrawing should stay in the paintcomponent method?
Is this correct or am I not fully understanding something?
I am assuming that you are not adding GUI components in the paintComponent method, right?
Components are not "removed" during a repaint
Better to put the stable part of any image into a single background BufferedImage and draw the BufferedImage in the paintComponent method. This can make for more efficient painting.
Consider calling an overload of repaint(...) that sets a bounding rectangle for the region to be painted.

Drawing on a jPanel that has sub-panels

I have a jPanel, which contains a number of sub-panels that can be dragged around. What I want to do, is to draw lines connecting some of those sub-panels together.
However, while it seems like this should be simple, it's proven very frustrating. The best I've gotten, is to override the paintComponent function in the original jPanel as such:
panCharDisplay = new javax.swing.JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
paintLines(g);
}
};
And then draw the lines as such:
public void paintLines(Graphics g) {
g.setColor(Color.BLUE);
for (Character c : characters) {
if (c.female && c.spouse != null) {
g.drawLine(c.display.getX(), c.display.getY(), c.spouse.display.getX(), c.spouse.display.getY());
}
}
}
This works in a sense, in that it does technically draw the lines, in the right place, the right color, and so on, but only if I scroll away from where the line should be, and then scroll back. Whenever I drag a component around it causes weird graphics errors, as it draws only parts of the line and doesn't erase the ones before. The lines also show up below the sub-panels instead of over them, making them hidden a lot of the time.
I assume the reason for this is that I'm drawing the lines at the wrong time, and need to draw them after drawing the sub-panels, and also make sure that they are re-drawn every time the panels are dragged around.
Is there another place I can put in an override to make the lines show up more consistently? Another method I tried, was to make a class that extends JPanel, and try to use that to handle the drawing, but I couldn't get it to work at all.
Whenever I drag a component around it causes weird graphics errors, as it draws only parts of the line and doesn't erase the ones before.
You probably need to invoke repaint() on the panel's parent as you drag the panel around.
The lines also show up below the sub-panels instead of over them, making them hidden a lot of the time.
You should be able to override either the paint() or paintChildren() method instead of the paintComponent() method. Whichever method you override make sure to invoke super.XXX() first so that the default painting is done before you attempt to draw your lines.
Personally I like the lines painted below the component as is demonstrated in trashgod's GraphPanel example. The example does custom painting for the shapes, but I would guess the logic would be similar for the components.
You can get this JConnector project and use as is or adapt the sources as you need.

My JApplet disappears when moved to secondary monitor

So I wrote a small program for a class. I designed it as a JApplet inside an undecorated JFrame, not in a browser. Other than that, it's a simple drawing program of sorts. You click two points to draw the selected shapes, then it calls a repaint. The problem I'm having is that when you draw while the program has been moved to my secondary monitor, the entire JApplet seems to disappear, only displaying the drawn shape. It only disappears after the 2nd point is selected, so I presume it does this on repaint().
My secondary monitor is using the exact same brand and resolution, even color profile.
Any other technical details, I'm using Java 1.7 (Can't recall which update off the top of my head), Windows 8 Enterprise 64x, using Eclipse's Run button to test.
Thanks in advance for any help!
I am indeed calling getGraphics(); in the init() method of the JApplet..
That is the problem. The Graphics object is a transient thing that will be repainted the very next time the JVM thinks there is any need to do so. That might be triggered by:
Changing the size or location of the window.
Covering it with another program and then removing the covering app.
Adding new components or changing values that are displayed.
See Performing Custom Painting for more details on how to do what you are attempting to achieve. OTOH Swing has a JLabel that can show a BufferedImage. You can use the BufferedImage in the way you want. When it is updated, call repaint() on the label to see the effect.

repaint() method causes a second, non-functional JMenuBar to appear?

I am creating a program in which the user can draw lines and erase them. I am able to create my window and create a functioning JMenuBar. When the drawing capability is not enabled, everything is fine. But when the drawing capability is enabled, a second JMenuBar is painted below the original. This menu bar is not functional, and sometimes when it is drawn, ALL of the graphics on the screen are repainted lower as well. Re-sizing the window in any way remedies these problem.
I would post pictures but, you know, I'm a new user and can't.
I have been able to narrow the problem down to a point in my paint() method. Need to know: When the mouse is dragged, the repaint() method is called. It uses my paint() method which calls the paintObject(Graphics g) method in my Line class. The JMenuBar error is appearing at the END of the paint method in the top class.
public void paint(Graphics g)//paint() in the top class
{
for(int i = 0; i < objs.size(); i++)//Loop that finds all Tool(Line) objects that
((Tools)objs.get(i)).paintObject(g);//have been drawn, and redraws them.
//This part of the code completes successfully with no error.
}//ERROR OCCURS HERE!!!!!!
I have no idea why this is happening. As a side note, there is also a JTextField on the screen that doesn't appear until it is highlighted. Thanks for at least reading this far!
Edit: I'll go on a limb, I'll bet you in fact are drawing directly on the top level window (JFrame or JApplet or JDialog...) and have added the mouse listener to this window as well -- hence the drawing of components translated down a bit.
The solution is to look at the tutorials first before attempting something fairly complex like this. They'll tell you how to draw correctly including the suggestions I've given in my comments, such as drawing in a JComponent or JPanel's paintComponent method and calling the super.paintComponent(g) method as the first method call of your paintComponent override.

Java JFrame / Canvas doesn't repaint

A mouse listener calls repaint() and I can see that it does actually go past the drawing part because I see globalcounter incremented in the output of System.out.println(). On the screen, however, I don't see any change until I minimize the window and maximize again, resize it, or move it out of the visible screen area and back in. Obviously I'd like it to update without my intervention.
class GUI extends javax.swing.JFrame {
int globalcounter=0;
class MyCanvas extends Canvas {
#Override
public void paint(Graphics g) {
globalcounter++;
g.drawString(globalcounter,100,100);
System.out.println(globalcounter);
}
}
}
(Originally I was loading an image from a file that got constantly updated (webcam) and painting it on the canvas. When I dragged it partly out of the visible screen area and back in, the part that has been 'outside' was refreshed, the rest not.)
revalidate() instead of repaint() didn't change anything.
I know this post is a duplicate to Java repaint not working correctly but when I posted it there it was deleted.
Why are you adding an AWT component, Canvas, to a Swing component, JFrame? You should stick with Swing components only. And also do you know the size of your MyCanvas, and how have you added it to the JFrame as you don't show this code.
Consider
using a JPanel instead of a Canvas object,
drawing in its paintComponent method,
showing us an sscce if you're still stuck.
And also, if all you're doing is drawing text, use a JLabel rather than drawing in paint/paintComponent, and change its text with its setText(...) method.

Categories

Resources