How does JVM call paint in Swing? - java

I'm new for AWT/Swing programming. I try to draw an image 100,000 times. The loop of drawImage() method is in paint() method. I find the times that java calls paint() method is quite unstable. It may call paint() method two times in one running time of program, but others may call three times. If I create a button that does repaint(), it would only call paint() one time each I click this button.
Could someone tell me when the paint() method would be called? I know System-triggered Painting and App-triggered Painting. But I think it doesn't belong to any of these cases.

You can use another thread to call the repaint() metho. An easy way to do this is to make your painting class (java.awt.Canvas or as here javax.swing.JPanel) implement Runnable and start that Thread in the constructor. Also add the run() method.
import javax.swing.*;
public class DrawPicktures extends JPanel implements Runnable
{
public DrawPickture()
{
/* Code */
new Thread(this).start();
}
public void Paint(Graphics g)
{
super.Paint(g); // Will draw all gui components added
/* Code */
}
public void run()
{
while(true)
{
repaint();
try
{
Thread.sleep(1000 /* Waits for 1000 milliseconds */);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
}
Because the JPanel (or Canvas) will be repainted from another thread it won't block up your main thread so your program can do other stuff at the same time.

Related

How to deal with SwingWorker thread that goes slow?

Hardly, do I understand multithreading stuff. And beginning look into it , I faced one issue, that came in my mind. I have recently wrote one simple application, and as soon as I get some new knowledge about Java I wish to improve my app with that I’ve learned.
It looks like simple swing GUI that updates images every period of time. I implemented ActionListener and overrode actionPerformed method. Timer with 15ms delay, repainted JPanelclass and everything worked fine. But I thought that updating my GUI using timer directly in actionPerformed (I presume that it’s another thread, but I’m barely sure) is bad idea. So I decided to change code and use SwingWorker. I called all my methods for my animation inside process() .. and again app working fine but it became extremely slow.
Now I’m thinking what’s wrong? Why it acts slower then before ? My timer delay is actually not waiting 15ms , it’s much slower even though delay the same. Am I made mistake with multithreading ?
Help me understand this stuff. Thanks in advance
public class GameEngine() extends SwingWorker<Void, Drawable>
GamePanel gp; // ref to JPanel class
{
public GameEngine(GamePanel gp)
{
this.gp = gp;
}
}
protected void doInBackground()
{
publish();
}
protected void process(List<Drawable> chunks)
{
Timer timer = new Timer(15, e ->
{
//methods for animation
fall();
animate();
checkTouch();
});
}
Some code I left beyond. If you need it I can write...
EDITION
Just for clarity of my issue I provide some more examples and addition explanation.
**Used to be: **
public class GamePanel extends JPanel
{
public void GamePanel()
{
GameEngine engine = new GameEngine(this);
}
//some images , variables etc...
protected void paintComponent(Graphics g)
super.paintComponent(g)
g.drawImage(image1, x, y, null);
g.drawImage(image2, w, z,null);
...
}
public class GameEngine () implements ActionListener
{
GamePanel gp;
Timer timer;
public void GameEngine(GamePanel gp)
{
this.gp = gp;
timer = new Timer( 15 , this );
}
public void actionPerformed()
{
//these methods repaint my GamePanel every 15ms.
fall(); // make object (image) increment on Y Axis
animate(); // make another object (image) decrement on X Axis
checkTouch(); // check if objects collided
}
}
**Became: **
public class GamePanel extends JPanel
{
public void GamePanel()
{
GameEngine engine = new GameEngine(this);
}
//some images , variables etc...
protected void paintComponent(Graphics g)
super.paintComponent(g)
g.drawImage(image1, x, y, null);
g.drawImage(image2, w, z,null);
...
}
public class GameEngine () extends SwingWorker<Void, Drawable>
{
GamePanel gp;
Timer timer;
public void GameEngine(GamePanel gp)
{
this.gp = gp;
}
protected void doInBackground()
{
process();
}
protected void progress()
{
timer = new Timer (15, e->
{
new ActionListener(new actionPerformed)
{
//these methods repaint my GamePanel every 15ms.
fall(); // make object (image) increment on Y Axis
animate(); // make another object (image) decrement on X Axis
checkTouch(); // check if objects collided
}
});
}
protected void done()
{
};
}
When I created it first I implemented ActionListener and updated my panel through timer declared in constructor.. I presumed that it’s thread-unsafe.
That’s why I transfer everything in progress method where I declared timer which ActionListener as lambda argument.
In other words I call all methods for animation in another thread.
Finally it became slower, comparing with first example..
I don’t understand
Is Timer from first example EDT or it’s another thread ?
My first example is thread safe ?
Why my second example goes much slower then first one ?
I heard about NOT update your GUI outside EDT, is it that case?
There is not enough information in your question to answer it, but I will take your queries about multi-threading and the implied question about Swing and rendering and see if I can help you out.
I think the most likely reason for your slowdown is unnecessary screen updating. Once a pixel has been drawn onto the canvas or whatever in your application, usually your application does not need to redraw it unless it is supposed to change; either a sprite moves or some other image in your app obscures a part of the drawing temporarily and then needs to be restored.
It is common for a novice repainting to ignore this, and just repaintng the entire painted surface. Although this will work, it is slow; if you're doing it a number of times in a loop, then the loop will seem slow.
The better way to do this is to use the rectangle passed into the redrawing routine and only repaint its intersection with the entire surface redrawn by your routine -- this cuts way down on the part that needs to be redrawn, and therefore on the time it takes to redraw it.
As for multithreading, I think it's helpful to think of it the way we used to think of things in a single-processor world -- the computer does something for a while, then stops in a place you cannot predict and does something in the other thread for a while, etc. You cannot assume the order in which things are going to get done, or how long it will spend on each thing, etc.
With modern multi-core computers, it is possible that these things in fact get done at the same time, but I don't know that trying to envision that helps you any.

Why does a MouseListener not function whilst a loop is running?

I am creating a java program and want to pause the program until a mouse click using a MouseListener is identified. How would I 'pause' the program, for example using a loop, in such a way that the MouseListener still works and the program can return to the same method?
I have tried putting a loop to stop the program until a variable is true, but the MouseListener cannot work if a loop is running.
I have also attempted to put the rest of my code in the mouseClicked method, or running new methods from within mouseClicked, however another error occurred as I cannot use Graphics g anywhere except for paintComponent, for this reason it seems to me that a loop is necessary to pause the program.
Here is a simplified program which I created to show the core of the problem. (Not full code).
class Surface extends JPanel implements MouseListener{
#Override
public void mouseClicked(MouseEvent arg0) {
//Some way to unpause program
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(30, 30, 200, 30);
//The program needs to 'pause' here until a click is identified.
System.out.println("Program finishes here");
}
}
The MouseListener does work, however it only seems to work if the program is dormant, and has finished all of the code in paintComponent.
Here is the code that does not work, as a loop is running.
class Surface extends JPanel implements MouseListener{
public static boolean repeatLoop = true;
#Override
public void mouseClicked(MouseEvent arg0) {
repeatLoop = false;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawLine(30, 30, 200, 30);
while (repeatLoop) {
}
System.out.println("Program finishes here");
}
}
I expected a loop telling the thread to sleep might work as well, but this has the same outcome and the MouseListener cannot be called.
Therefore I am asking why can a MouseListener not function when a loop is running, and is there an easy way to avert this problem and pause the program until the mouseClicked code is run.
Your loop in the paintComponent blocks the main thread, this is why this won't work. You shouldn't put that kind of logic in the paintComponent. The best thing you could do is create a separate Thread which checks for the repeatLoop. If the reapetLoop variable gets to false you could finish the application.
The paintComponent method is there just to paint on the JPanel.

How can I create an animation method and put that in paint method? My code doesn't work

Why doesn't this work? I created a method for an animation loop of two pictures called animation(),
and i want to put that in the paint method. How do i do this?
import java.applet.*;
import java.awt.*;
public class Slot_Machine extends Applet
{
Image back;
int coin=10;
// Place instance variables here
public void init ()
{
back= getImage(getDocumentBase(),"Images/Background/Slot Machine.png");
// Place the body of the initialization method here
} // init method
public void animation(Graphics g)
{
Image Title1,Title2;
Title1= getImage(getDocumentBase(),"Images/Title Animation/Title 1.png");
Title2= getImage(getDocumentBase(),"Images/Title Animation/Title 2.png");
while(true){
g.drawImage(Title2,200,0,this);
{ try { Thread.currentThread().sleep(2000); }
catch ( Exception e ) { } }
g.drawImage(Title1,200,0,this);
{ try { Thread.currentThread().sleep(2000); }
catch ( Exception e ) { } }
}//end while(true) loop
}//end animation
public void paint (Graphics g)
{
g.drawImage(back,0,0,this);
animation(); //FROM THE METHOD ABOVE, WHY DOESNT THIS WORK? HOW DO I FIX THIS?
String coins = String.valueOf(coin);
g.setColor(Color.white);
g.setFont(new Font("Impact",Font.PLAIN,30));
g.drawString(coins,405,350);
// Place the body of the drawing method here
} // paint method
}
Your animation() method has an infinite loop in it, which means that paint() never returns, which is causing the thread trying to start up the applet to hang while waiting for paint() to return.
If you want to run a forever-looping animation in the background, try doing it in another thread. Take a look at ExecutorService.

Error in a simple applet

The following code is supposed to print
inside init()-- inside start()-- inside paint().
But it prints the last part inside paint() TWICE! Why is that?
public class SampleApplet extends Applet {
String msg;
#Override
public void init(){
setBackground(Color.BLACK);
setForeground(Color.yellow);
msg = "Inside init()-- ";
}
#Override
public void start(){
msg += "Inside start()-- ";
}
#Override
public void paint(Graphics g){
msg += "Inside paint().";
g.drawString(msg, 10, 30);
}
}
Quoted from: Paint():
the paint() method will be called as many times as necessary. If you
put another window over your GUI then the paint() method will be
called. If you then minimize that window and make your GUI visible
again then the paint() method will be called again. And so on.
So if you have something that is a problem if the paint() method is
called more than once, you have done it wrong. Don't do it that way.
The paint() method should only redraw its target from existing data,
it should never have to do calculations to figure out what to paint.
The paint method may be called by update when the component needs to repaint the content of the component state is invalidated.

Doing Live/Dynamic changes in Swing

I am making a game having squares in it (a grid of panels) and when the game ends there is an algorithm that changes the color of the panels one by one in a "live" fashion where the user watches the squares change color slowly. I try something like:
Thread.sleep(1000);
grid.getComponent(boxNumber).setBackground(Color.YELLOW);
Thread.sleep(1000);
grid.getComponent(boxNumber).setBackground(Color.ORANGE);
Although the color of a box changes to Yellow, it does not change to Orange afterwards. Anyone have any ideas? Hope I was able to be clear.
Read the section from the Swing tutorial on Concurrency to understand why you should not be using the sleep() method.
One solution is to use a SwingWorker, then you can 'publish' the color of the component so it can be updated properly on the EDT and you can invoke the sleep() method in the worker as well.
These need to happen on the Swing event thread. call set background via:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
grid.getComponent(boxNumber).setBackground(Color.ORANGE);
}
});
Note, your Thread.sleep() should not be in the event thread (or directly from within a Swing event listener (ActionListener, WindowListener, etc).
It would also be prudent to look the Swing Timing Framework which is specifically for things like this.
-Generally its not a good idea to do Thread.sleep(1000); in the EDT. You should try using Timers.
-You also need to call revalidate()/validate() and repaint() afterward.
So maybe something like this:
Timer yellowTimer = new Timer(1000,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jtp.setBackground(Color.YELLOW);
//call revalidate()/validate() and repaint() afterward
jtp.revalidate();
jtp.repaint();
}
});
yellowTimer.setRepeats(false);
Timer orangeTimer = new Timer(2000,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jtp.setBackground(Color.ORANGE);
//call revalidate()/validate() and repaint() afterward
jtp.revalidate();
jtp.repaint();
}
});
orangeTimer.setRepeats(false);
yellowTimer.start();
orangeTimer.start();

Categories

Resources