Reputation:
My objective is to put a button and a circle on the JFrame. When i click the button the circle should move randomly on the panel/frame.
But when i click the button the circle just move once only and after putting SOP statements i found that "frame.repaint()" is getting called multiple times but this call is triggering the "paintComponent" method only once, the very first time (defined in class Panel1). Its very strange!
I have also provided another code which works as expected but has no buttons to trigger the animation. I have read that repaint() requests are coalesced together and executed once, then how come the second program works?
import java.awt.event.*;
import java.awt.Graphics.*;
import javax.swing.*;
import java.awt.*;
public class SimpleGui3c_4 {
public static void main(String args[]) {
Frame1 frame = new Frame1();
frame.go();
}
}
class Frame1 {
JFrame frame;
Panel1 p;
void go() {
frame = new JFrame();
JButton button1 = new JButton("Color Change");
p = new Panel1();
frame.setSize(500,500);
frame.setVisible(true);
frame.getContentPane().add(BorderLayout.SOUTH, button1);
frame.getContentPane().add(BorderLayout.CENTER, p);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
button1.addActionListener(new ColorActionListener());
}
class ColorActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i=0;i<130;i++) {
System.out.println("Frame repaint started");
frame.repaint();
try {
Thread.sleep(5000);
}catch(Exception ex) {}
System.out.println("Frame repaint ended");
}
}
}
class Panel1 extends JPanel {
public void paintComponent(Graphics g) {
System.out.println("Inside the paint Component method");
int x = (int)(Math.random()*100);
int y = (int)(Math.random()*100);
g.setColor(Color.BLUE);
g.fillOval(x,y,100,100);
System.out.println("Exiting the paint component method");
}
}
}
Code which works but has no button to trigger the animation, it works as soon as i run the code. I am not sure why the below program works and the above program fails!
import javax.swing.*;
import java.awt.*;
public class SimpleAnimation {
int x = 70;
int y = 70;
public static void main(String args[]) {
SimpleAnimation gui = new SimpleAnimation();
gui.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(drawPanel);
frame.setSize(300,300);
frame.setVisible(true);
for(int i = 0;i<130;i++) {
//x++;
//y++;
drawPanel.repaint();
try {
Thread.sleep(50);
}catch(Exception ex) {}
}
}//close go
class MyDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0,0,this.getWidth(), this.getHeight());
int x = (int)(Math.random()*70);
int y = (int)(Math.random()*70);
g.setColor(Color.green);
g.fillOval(x,y,40,40);
}
}
}
Upvotes: 0
Views: 48
Reputation: 347204
I have also provided another code which works as expected but has no buttons to trigger the animation.
The difference between the two pieces of code is context within which they are been called.
The code that "works" is actually been updated out side the context of the Even Dispatching Thread, in the "main" thread, which means that doing something like Thread.sleep
won't prevent the UI from been updated.
The code which does not work is been updated from with the content of the Event Dispatching Thread (from within the ActionListener
), which is prevent the EDT from processing new paint requests until after the actionPerformed
method returns
Another issue you will face relates to when you decide to update the position of the circle.
paintComponent
can be called for all a number of different reasons, many which you don't control. Painting should focus on painting the current state and should never modify it (directly or indirectly). Instead, you should use some kind of update
method, whose job it is, is to update the x/y position of the circle and trigger a new paint cycle.
I would highly recommend that you stop and take the time to read through:
Your problem is rookie mistake which comes about from not understanding how the API actually works and not understanding the tools available to solve it
There are a number of other "issues" which would result in undesirable behaviour, like not calling setVisible
last, so the UI doesn't need be updated again to ensure that the components been added are visible.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimpleGui3c_4 {
public static void main(String args[]) {
new SimpleGui3c_4();
}
public SimpleGui3c_4() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Frame1 frame = new Frame1();
frame.go();
}
});
}
public interface Animatable {
public void update();
}
public class Frame1 {
JFrame frame;
Panel1 p;
void go() {
frame = new JFrame();
JButton button1 = new JButton("Color Change");
p = new Panel1();
frame.getContentPane().add(BorderLayout.SOUTH, button1);
frame.getContentPane().add(BorderLayout.CENTER, p);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
button1.addActionListener(new ColorActionListener(p));
frame.pack();
frame.setVisible(true);
}
class ColorActionListener implements ActionListener {
private Animatable parent;
public ColorActionListener(Animatable parent) {
this.parent = parent;
}
public void actionPerformed(ActionEvent e) {
JButton btn = (JButton) e.getSource();
btn.setEnabled(false);
Timer timer = new Timer(5000, new ActionListener() {
private int counter = 0;
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Frame repaint started");
parent.update();
counter++;
if (counter >= 130) {
((Timer)e.getSource()).stop();
btn.setEnabled(true);
}
}
});
timer.setInitialDelay(0);
timer.start();
}
}
class Panel1 extends JPanel implements Animatable {
private int xPos, yPos;
public Panel1() {
update();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Inside the paint component method");
g.setColor(Color.BLUE);
g.fillOval(xPos, yPos, 100, 100);
System.out.println("Exiting the paint component method");
}
@Override
public void update() {
System.out.println("Inside the update method");
xPos = (int) (Math.random() * 100);
yPos = (int) (Math.random() * 100);
repaint();
}
}
}
}
Upvotes: 1