Hello I i'm having random flicker every couple of seconds when running my program, it has a single image moving across screen. I'm using Graphics paint() method and repaint() in the thread's run() method. Here are the relevant parts of the code, i'll post entire code if necessary. Btw, pawns is an arraylist loaded with pawn objects, originally I had 5 threads for 5 images moving across but I tried with only 1 image and it still flickers so it's not that.
private BufferedImage helicopter;
helicopter = ImageIO.read(new File("white.png"));
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
for(count=0; count<pawns.size(); count++){
g.drawImage(helicopter, pawns.get(count).getX(), pawns.get(count).getY(), null);
}
g.setColor(Color.BLACK);
g.drawLine(350, 0, 350, 600);
}
public void run() {
while(true) {
randSleep = (int)(Math.random()*100);
randMove = (int)(Math.random()*2);
pawn.setX(pawn.getX()+randMove);
try{
Thread.sleep(40);
}
catch(Exception e) {
e.printStackTrace();
}
repaint();
}
}
On components with complex output, repaint() should be invoked with
arguments which define only the rectangle that needs updating, rather
than the no-arg version, which causes the entire component to be
repainted.
Swing's implementation of paint() factors the call into 3 separate
callbacks: paintComponent() paintBorder() paintChildren() Extensions
of Swing components which wish to implement their own paint code
should place this code within the scope of the paintComponent() method
( not within paint()).
source: Painting in AWT and Swing: Good Painting Code Is the Key to App Performance
You should notice in the source quoted and linked that repaint() (no arguments) will call update(), which by default clears the background before drawing. I suspect that this is the source of the flicker, when the component is cleared before calling paint().
If you are using Swing components you should not implement your own double buffering but instead use the functionality provided by Swing.
First try, calling a repaint with arguments to avoid update clearing the entire background. Or write an override for the update method. If that does not solve the problem next try putting your drawing code in the paintComponent method of a Swing component.
Related
This is The Code :
public void paint(Graphics g) {
g.fillOval(x, y, 25, 25);
repaint();
}
This will create an output like This where if i move the pointer it doesn't clear the previous Graphics and creates a Path .
Output of Above Code
And by adding the super statement like this it Doesn't show the Path but only the Current location of the Oval Graphic.
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 25, 25);
repaint();
}
The output is This
I just want to the Mechanism and The Reason Behind This.
Sorry, but there is so much wrong with your code, that we have to fix it:
First off, don't override a JComponent's (or JFrame's) paint method but rather override the JComponent's or JPanel's paintComponent. This way you avoid mistakingly messing up painting of borders or child components, both of which paint is responsible for. Also since paintComponent has automatic double buffering, your animation will be much smoother.
Next, you almost always do want to call the super's painting method, which for paintComponent is super.paintComponent(g) as this will continue to propagate the painting chain, whereas by not calling this you break this chain. Calling the super's method will call the component's housekeeping painting including the overpainting of "dirty" pixels which is why the trails are removed. This is likely the answer that you're looking for.
Most important never call repaint() from within any painting method. Doing this results in a poor-man's animation that is completely uncontrollable, and puts too much responsibility on the painting method which should focus on its sole job -- to paint the component. Use a game loop such as a Swing Timer instead.
Most important -- read the tutorials as most of this is already explained there:
Lesson: Performing Custom Painting: introductory tutorial to Swing graphics
Painting in AWT and Swing: advanced tutorial on Swing graphics
Straight from the docs
public void paint(Graphics g)
Paints the container. This forwards the paint to any lightweight
components that are children of this container. If this method is
reimplemented, super.paint(g) should be called so that lightweight
components are properly rendered. If a child component is entirely
clipped by the current clipping setting in g, paint() will not be
forwarded to that child.
As he said that super.paint() cleans the dirty pixels. This says it all .!! :)
Without going into grave detail, I'm working toward creating a desktop-like program in Swing, with icons drawn on top of a background image. Usually I subclass JPanel or another JComponent and draw on that, but wanted to try something new just for kicks, and tried drawing on an instance of JFrame, without making my program a subclass of it.
I am aware that this is not the accepted way of doing this, but discovering that the image was not drawn has exposed a missing link (one of the many, I suppose) in my understanding of Swing and how it paints components.
What confuses me is that if my program subclasses JFrame and I override the paint() method (the accepted way, in other words), it will draw the image into the JFrame, but it will not do this for an instance of JFrame in my non-subclassed program.
Hopefully the code showing essentially what I want to do will help:
public class ImageLoader
{
BufferedImage img = null;
JFrame window = null;
public ImageLoader()
{
try
{
img = ImageIO.read(new File("src/strawberry.jpg"));
}catch(IOException e)
{
e.printStackTrace();
}
window = new JFrame("Strawberry Viewer");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
//Why can't I do something akin to the following to draw on an instance of JFrame?
Graphics g = window.getGraphics();
paint(g);
}
public void paint(Graphics g)
{
g.drawImage(img, 0, 0, null);
}
public static void main(String[] args)
{
new ImageLoader();
}
}
I have read Oracle's page "Painting in AWT and Swing" but I'm still not understanding why I can't draw on an instance of JFrame. Is there any situation where I could draw on an instance of a component, or do they all have to be subclassed if I want to draw on them?
Finally, if the problem is based largely on my gross misunderstanding of how Swing works, what are some recommended books or other resources for understanding Swing?
Thanks for the help in advance. I appreciate it.
Don't use getGraphics() to do painting.
Anything you do with that Graphics object will only be temporary. Then next time Swing determines the frame needs to be repainted you will lose the painting.
In your case you use pack() so the frame is minimized. When you resize the frame, the normal frame painting will paint over your image. So try using setSize(500, 500);.
However even this won't work because your image will be painting before the normal painting has completed. Not all code is executed sequentially.
Try the following to delay the painting of the image:
try
{
Thread.sleep(1000);
Graphics g = window.getGraphics();
paint(g);
}
catch(Exception e) { e.printStackTrace(); }
When the image shows, then try resizing the frame and you will lose the image.
if my program subclasses JFrame and I override the paint() method
Don't override paint() of a JFrame (yes, it will work, but it is NOT the way painting was designed to be done in Swing). Custom painting is done by overriding paintComponent() of a JPanel and then you add the panel to the frame.
if the problem is based largely on my gross misunderstanding of how Swing works
The Swing tutorial is the best place to start for Swing basics. See the section on Custom Painting to get your started.
For a more technical article see Painting in AWT and Swing.
I am sorry about the non-descriptive title but I am not sure how to communicate the problems. For starters the JButtons every now and again are creating themselves multiple times in the same order that the loop should create them. Another issue I am having is that when I reposition them using the setLocation() method it creates new JButtons where I want them but also leaves the old ones where they are. I don't know if I just have to refresh the graphics or what is going on. Thanks for the help.
the array playerHand()is defined in the Player class and is 5 in length.
public void paintComponent(java.awt.Graphics g){
setBackground(Color.GREEN);
// create a Graphics2D object from the graphics object for drawing shape
Graphics2D gr=(Graphics2D) g;
for(int x=0;x<Player.hand.size();x++){
Card c = Player.hand.get(x); //c = current element in array
c.XCenter = 30 + 140*x;
c.XCord = c.XCenter - 30;
c.YCord = 0;
//5 pixel thick pen
gr.setStroke(new java.awt.BasicStroke(3));
gr.setColor(Color.black); //sets pen color to black
gr.drawRect(c.XCord, c.YCord, c.cardWidth-1, c.cardHeight-1); //draws card outline
gr.setFont(new Font("Serif", Font.PLAIN, 18));
gr.drawString(c.name, c.XCord+10, c.YCord+20);
gr.drawString("Atk: ", c.XCord+10, c.YCord+60);
gr.drawString(""+c.attack, c.XCord+60, c.YCord+60);
gr.drawString("Def: ", c.XCord+10, c.YCord+80);
gr.drawString(""+c.defence, c.XCord+60, c.YCord+80);
gr.drawString("HP: ", c.XCord+10, c.YCord+100);
gr.drawString(""+c.health, c.XCord+60, c.YCord+100);
JButton button = new JButton(c.name);
button.setSize(c.cardWidth, c.cardHeight);
//button.setLocation(c.XCord, c.YCord);
this.add(button);
repaint();
}
} //end of paintComponent
There are several problems (marked by "!!!!" comments) in your method below:
public void paintComponent(java.awt.Graphics g){
setBackground(Color.GREEN); // !!!!
Graphics2D gr=(Graphics2D) g;
for(int x=0;x<Player.hand.size();x++){
// .... etc ....
JButton button = new JButton(c.name); // !!!! yikes !!!!
button.setSize(c.cardWidth, c.cardHeight);
//button.setLocation(c.XCord, c.YCord);
this.add(button); // !!!! yikes !!!!
repaint(); // !!!!
}
}
Do not add components to GUI's in paintComponent(...). Never do this. Ever.
This method should be for drawing and drawing only and never for program logic or GUI building.
You do not have full control over when or if it will be called.
It needs to be as fast as possible so as not to make your program poorly responsive.
Avoid object creation within paintComponent(...) if possible.
Avoid file I/O in this method (though not a problem with your code above).
Don't call setBackground(...) in this method. Do that in the constructor or other method.
Never call repaint() from within this method.
Don't forget to call the super's paintComponent(g) method in your override.
To go over your problems:
For starters the JButtons every now and again are creating themselves multiple times in the same order that the loop should create them.
This is because you do not have control over when or how often paintComponent is called. The JVM may call it in response to information from the operating system about a dirty region, or the program may request a repaint, but for this reason, program logic and gui building should never be in this method.
Another issue I am having is that when I reposition them using the setLocation() method it creates new JButtons where I want them but also leaves the old ones where they are.
Quite likely you're seeing image remnants from your not calling the super's paintComponent method. By not doing this, you don't have the JPanel repaint itself and remove old image pixels that need to be replaced or refreshed.
I don't know if I just have to refresh the graphics or what is going on.
No, you need to change just about everything.
It looks like you have to re-think your GUI's program flow and logic and re-write a bit of code.
Here is simple example of drawing an oval.
public class SwingPainter extends JFrame{
public SwingPainter() {
super("Swing Painter");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().add(new MySwingComponent());
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args) {
new SwingPainter();
}
class MySwingComponent extends JComponent {
public void paintComponent(Graphics g) {
System.out.println("paintComponent");
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(10, 10, 50, 50);
}
#Override
protected void paintBorder(Graphics g) {
System.out.println("Paint border");
super.paintBorder(g);
}
#Override
protected void paintChildren(Graphics g) {
System.out.println("Paint children");
super.paintChildren(g);
}
}
}
But in debug mode or adding some info to console before drawing (as in example), you can see that swing draws components twice.
paintComponent
Paint border
Paint children
paintComponent
Paint border
Paint children
I cannot understand why it happens, but I think it can affect performance in a difficult GUI.
The article Painting in AWT and Swing: Additional Paint Properties: Opacity suggests why: "The opaque property allows Swing's paint system to detect whether a repaint request on a particular component will require the additional repainting of underlying ancestors or not." Because you extend JComponent, the opaque property is false by default, and optimization is not possible. Set the property true to see the difference, as well as the artifact from not honoring the property. Related examples may be found here and here.
I agree with Konstantin, what you need to remember, the repaint manager is responding to any number of requests when the application starts, this typically includes the initial paint request when the window is shown and resized (there's two).
Try this. Wait till the application is running and resize the window. I'm sure you'll get more then a couple of repaint requests ;)
This works fine to me. In fact, even in debug mode the output was:
paintComponent
Paint border
Paint children
Please, bear in mind that in AWT and Swing components there are many methods (paint, paintBorder, paintChildren, paintComponent, repaint, and others) that are called via call-back, whenever the GUI engine finds suitable. That may vary from JVM to JVM or even from different execution sessions. They can also be triggered from the interaction to your program (if you minimize/maximize, for example). Or they may not, at all.
I'm creating a game and I want some of the image to be repainted while others to stay constant. I had put my methods in the paint() on java applet but this seems to access the methods in an endless loop.
How do I create a "driver method" that will access my methods but also use the draw() at the same time?
public void paint (Graphics g)
{
bufferGraphics.clearRect (0, 0, dim.width, dim.height);
//mainScreen ();
g.drawImage (offscreen, 0, 0, this);
} // end Paint method
public void update (Graphics g)
{
paint (g);
}
public void main (String[] args)
{
game ();
}
Overriding paint() and update() is done when using AWT. Since you are just learning about painting why not write a Swing applet and extend JApplet, since Swing is more commonly used these days? Then custom painting is done by extending JPanel or JComponent. Then you add this component to the content pane of the JApplet, just like it was a JFrame.
Read the section from the Swing tutorial on Custom Painting for more examples of painting with Swing.