Why isn't my animation visible? - java

So this is my code:
import javax.swing.*;
import java.awt.event.*;
public class Frame {
Draw d = new Draw();
JFrame f1 = new JFrame("Animation 2");
JButton bMoveRight = new JButton(">>>>");
JButton bMoveLeft = new JButton("<<<<");
public Frame() {
f1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f1.setSize(800, 600);
f1.setVisible(true);
f1.setResizable(false);
f1.setLocationRelativeTo(null);
bMoveRight.setBounds(50, 450, 120, 50);
bMoveLeft.setBounds(600, 450, 120, 50);
f1.add(bMoveRight);
f1.add(bMoveLeft);
f1.add(d);
bMoveRight.addActionListener(new ButtonMoveRight());
bMoveLeft.addActionListener(new ButtonMoveLeft());
}
private class ButtonMoveRight implements ActionListener {
public void actionPerformed(ActionEvent e){
d.animateRight();
}
}
private class ButtonMoveLeft implements ActionListener {
public void actionPerformed(ActionEvent e){
d.animateLeft();
}
}
}
import javax.swing.*;
import java.awt.*;
public class Draw extends JComponent{
int x = 50;
public void paint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(x, 150, 200, 100);
}
public void animateLeft(){
try{
while(x != 50){
x--;
repaint();
Thread.sleep(10);
}
} catch(Exception ex){
ex.printStackTrace();
}
}
public void animateRight(){
try{
while(x != 550){
x++;
repaint();
Thread.sleep(10);
}
} catch(Exception ex){
ex.printStackTrace();
}
}
}
Everything works as it should. Except one thing. My animation happens but the problem is it doesn't show. I made another program where there is only animation and it starts right away,but in this one I made buttons to start the animations. What happens is I click the button and nothing happens for 5 seconds (that's the time it needs to get to the other side) and after 5 seconds it appears on the other side of the window. Why won't my animation show?

The Thread.sleep() inside your "ActionListener" callbacks is strongly discouraged. The issue is the following: your code is invoked in the same thread which is main thread for GUI application - it is used to draw your interface.
What you need to do instead of sleep() invocation - you can fire some events with delay and process right way. You have to use something like timer - probably this one https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Related

Is there a more performance friendly way to iterate through images for an animation than using an imageIcon array in Java?

I'm Trying to Animate a seagull image on a JPanel for a game I'm building. The animation works great, it looks how I want it to, but when I click the play button to switch cards to the next JPanel, (There is a play button controlled by the JFrame containing this panel that changes the card to another JPanel, this lags when the animation is going) it lags when the animation is running. Is there a more performance friendly way to run my animation than by looping through an ImageIcon[] like I'm doing?
public class StartPanel extends JPanel implements ActionListener{
Image gull;
Timer timer;
ImageIcon[] seagullAnimation = new ImageIcon[9];
int counter = 0;
StartPanel(){
seagullAnimation[0] = new ImageIcon(getClass().getResource("gullBlink0.png"));
//Here I add the rest of the images in the same fashion
timer = new Timer(13, this);
timer.start();
setPreferredSize(new Dimension(500,500));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//Iterates through the Gull Blink images and set the proper image
if(counter >=8){
counter = 0;
timer.stop();
try {
//Randomizes sleep times to make blink look natural
Thread.sleep((long) (Math.random() * 5000));
timer.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
counter++;
}
gull = seagullAnimation[counter].getImage();
g.drawImage(gull,10,-450, this);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}}
Fixed the Lag. Removed the code that slept the Event Dispatch thread, which seemed to be causing the lag and used the timer to handle the pause between animations.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
setGullFrame();
gull = seagullAnimation[counter].getImage();
g.drawImage(gull,10, -450, this);
public void setGullFrame(){
if(counter >=8){
counter = 0;
timer.stop();
timer.setInitialDelay((int) (Math.random() * 5000));
timer.start();
} else {
counter++;
}
}

Change backround color of JPanel multiple times

I am trying to make a little program that includes changing the color of a Panel time-based.
Right now I am just trying to do that part without the rest. So I just wrote a little Interface with only one panel and I want to change the color within a loop multiple times.
The problem is, even though the thread pauses for the correct amount of time, the color of the Panel doesn't change correctly. It changes just sometimes in the loop not every time.
my Interface Class:
import javax.swing.*;
import java.awt.*;
//creates the Interface
public class Interface extends JFrame {
private JPanel frame1;
public Interface (String titel) {
super(titel);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame1 = new JPanel();
this.frame1.setPreferredSize(new Dimension (200, 200));
setLayout(new FlowLayout());
add(frame1);
this.setVisible(true);
}
public JPanel getFrame1() {
return frame1;
}
}
my Pause Class:
import java.util.TimerTask;
//supposed to pause the thread by #pause amount of milliseconds
public class Pause extends TimerTask {
private int pause;
public Pause(int pause){
this.pause = pause;
}
#Override
public void run() {
System.out.println("Timer"+ pause+" task started at:"+System.currentTimeMillis());
pause();
System.out.println("Timer task"+ pause+" ended at:"+System.currentTimeMillis());
}
public void pause() {
try {
Thread.sleep(this.pause);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
my Blink Class
import javax.swing.*;
import java.awt.*;
public class Blink {
private JPanel frame1;
public Blink(Interface anInterface){
this.frame1 = anInterface.getFrame1();
}
// blink should change the color of the JPanel inside my Frame.
// Its supposed to change to red for 200 ms
// and then to white again for 1000 ms.
// this should be repeated 10 times.
public void blink() {
Pause pause1 = new Pause(200);
Pause pause2 = new Pause(1000);
pause2.run();
int i = 1;
while(i <= 10){
i++;
frame1.setBackground(Color.red);
frame1.repaint();
pause1.run();
frame1.setBackground(Color.white);
frame1.repaint();
pause2.run();
}
}
public static void main ( String[] args ) {
Interface anInterface = new Interface("Title");
anInterface.setVisible(true);
Blink blink = new Blink(anInterface);
blink.blink();
}
}
According to Concurrency to Swing you cannot simply Thread.sleep the Thread where the GUI runs because it will freeze it, hence events cannot take place. Instead, for any kind of animation or long-heavy task (consider Thread.sleep as one), Swing Timers and Swing Workers should be used. In your case, a javax.swing.Timer fits better.
One example of its usage:
public class Blink {
private JPanel frame1;
private int pause1TimesRan;
private int pause2TimesRan;
private Timer pauser1, pauser2;
public Blink(Interface anInterface) {
this.frame1 = anInterface.getFrame1();
//Create pauser 1 with delay 200ms
pauser1 = new Timer(200, e -> {
if (pause1TimesRan == 10) {
pauser1.stop();
return;
}
Color color = randomColor();
frame1.setBackground(color);
System.out.println("Pauser #1 changed background to: " + color);
pause1TimesRan++;
});
//Create pauser 2 with delay 1000ms
pauser2 = new Timer(1000, e -> {
if (pause2TimesRan == 10) {
pauser2.stop();
return;
}
Color color = randomColor();
frame1.setBackground(color);
System.out.println("Pauser #2 changed background to: " + color);
pause2TimesRan++;
});
}
private static Color randomColor() {
return new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
}
public void blink() {
pauser1.start();
pauser2.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Interface anInterface = new Interface("Title");
anInterface.setVisible(true);
Blink blink = new Blink(anInterface);
blink.blink();
});
}
static class Interface extends JFrame {
private JPanel frame1;
public Interface(String titel) {
super(titel);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame1 = new JPanel();
this.frame1.setPreferredSize(new Dimension(200, 200));
setLayout(new FlowLayout());
add(frame1);
this.setVisible(true);
}
public JPanel getFrame1() {
return frame1;
}
}
}
One off-topic advice is to name your methods (and variables) properly. You called the method getFrame1(), but it is actually a JPanel and not a JFrame. So, a better name could be getPanel(). Also, about the SwingUtilities.invokeLater part, read What does SwingUtilities.invokeLater does.

Java drawing a circle using awt,JFrame with Thread(Runnable) on Netbeans (Mac)

https://github.com/terryaa/KOSTA_MAC/tree/master/Java/NetBeans/day13_01_15/src/ex1
What I'm trying to do is to draw circles, but one circle on a canvas at a time and then moving on to drawing next circle using Runnable join. It should draw a circle using .start() and the other .start() shouldn't start until formal .start()'s drawing circle is done.
In linked page's package, Ex3_Canvas1 class has main and use Runnable MyThread0 class to draw a circle using basic .start() and .join() and it does perfectly what I want.
I created NetBean's automatic JFrame class Ex2_CanvasDemo and tried to do the same and failed. JFrame window pops up after drawing a full circle and then shows creating of next circle. What I want is that the window should first appear and it shows creation of both circles ,not simulataneously but sequently, like Ex3_Canvas1.
I guess it's because main thread waits for th(Ex2_CanvasDemo) to finish so window doesn't apply for changes. But shouldn't Ex1_Canvas1 should do the same? Is this differences due to automatically generated code by netbeans? How can I do the same as Ex1_Canvas1 in Ex2_CanvasDemo.
I tried making a Runnable class and used in Ex2_CanvasDemo but failed also..
Any help?
I'm using jdk 8 and netbeans8 on mac.
--Thread part of Ex2_CanvasDemo--
public Ex2_CanvasDemo() {
initComponents();
Thread th=new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<370;i+=10){
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
}
arcNUm=i;
System.out.println("circle"+arcNUm);
canvas1.repaint();
}
}
});
th.start();
try {
th.join();
} catch (InterruptedException ex) {
Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
}
th=new Thread(new Runnable() {
#Override
public void run() {
for(int i=0;i<370;i+=10){
System.out.println("circle"+i);
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
}
arcNum2=i;
canvas2.repaint();
}
}
});
th.start();
// try {
// th.join();
// } catch (InterruptedException ex) {
// Logger.getLogger(Ex2_CanvasDemo.class.getName()).log(Level.SEVERE, null, ex);
// }
}
Caveat
Animation is hard, good animation is really hard. You need to repeat this to yourself, because animation done right is, really hard.
What you need to know...
Animation is basically the illusion of change over time. Very rarely do you want to perform linear animation, animation is normally done over a period of time, as it allows for performance difference in the platform to be smoothed out in away which is not as harsh to the user.
Swing is single threaded and not thread safe. This means that you should not block the event dispatching thread and that you must only update the UI from within the context of the event dispatching thread.
See Concurrency in Swing for more details
This makes life a little difficult, as you can't simply run a linear loop in the EDT, as this will block the UI and it's difficult to do it from a Thread because it's a mess of synchronisation.
One of the simplest solutions is to make use of the available API and use a Swing Timer, which acts as a pseudo loop, putting in a small delay between call backs, in which you can perform some operations
Example...
While there are a number of ways you might approach this, I've set up a simple List which contains a bunch of Animatables which "do stuff" and then simply run one after the other in serial.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
TestPane testPane = new TestPane();
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(testPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
testPane.play();
}
});
}
});
}
public class TestPane extends JPanel {
private List<Animatable> animations;
private Animatable animation;
private Timer timer;
public TestPane() {
animations = new ArrayList<>(25);
animations.add(new CircleAnimation(Color.RED));
animations.add(new CircleAnimation(Color.BLUE));
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (animation == null) {
animation = animations.remove(0);
}
if (animation.update(getBounds())) {
if (animations.isEmpty()) {
((Timer)e.getSource()).stop();
} else {
animation = animations.remove(0);
}
}
repaint();
}
});
}
public void play() {
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (animation != null) {
Graphics2D g2d = (Graphics2D) g.create();
animation.paint(g2d);
g2d.dispose();
}
}
}
public interface Animatable {
public boolean update(Rectangle bounds);
public void paint(Graphics2D g2d);
}
public class CircleAnimation implements Animatable {
private Color color;
private Ellipse2D circle;
private double delta = -1;
public CircleAnimation(Color color) {
this.color = color;
}
#Override
public boolean update(Rectangle bounds) {
if (circle == null) {
circle = new Ellipse2D.Double(bounds.width, (bounds.height / 2) - 10, 20, 20);
}
Rectangle rect = circle.getBounds();
rect.x += delta;
circle.setFrame(rect);
return rect.x + 20 < bounds.x;
}
#Override
public void paint(Graphics2D g2d) {
if (circle == null) {
return;
}
g2d.setColor(color);
g2d.fill(circle);
}
}
}

My game is flickering due to my painting directly in JFrame. How do I use JPanel? [duplicate]

I think I need to put some code where the comment is (or maybe use non static method but I am not sure). The main method creates the window and then starts the graphics method. I would like the blue square to flash.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class paintTest extends JPanel{
private static JFrame theWindow = new JFrame("Window");
static boolean blueSqr = false;
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillRect(10, 10, 10, 10);
if(blueSqr){
g.setColor(Color.BLUE);
g.fillRect(10, 10, 10, 10);
}
}
public static void main(String[] args){
createWindow();
theWindow.getContentPane().add(new paintTest());
while(true){
blueSqr = false;
System.out.println("off");
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
blueSqr = true;
// Needs something here
System.out.println("on");
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
}
}
public static void createWindow(){
theWindow.setSize(500, 500);
theWindow.setLocationRelativeTo(null);
theWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theWindow.setVisible(true);
}
}
Any help would be really good.
Use a Swing Timer to call repaint(). Also, override paintComponent() in a JPanel, rather than paint().
Something like this:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PaintTest extends JPanel{
boolean blueSqr = false;
PaintTest() {
setPreferredSize(new Dimension(100,25));
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
blueSqr = !blueSqr;
repaint();
}
};
Timer timer = new Timer(1000,al);
timer.start();
}
public void paintComponent(Graphics g) {
Color c = (blueSqr ? Color.BLUE : Color.RED);
g.setColor(c);
g.fillRect(10, 10, 10, 10);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame theWindow = new JFrame("Window");
theWindow.getContentPane().add(new PaintTest());
createWindow(theWindow);
}
});
}
public static void createWindow(JFrame theWindow){
theWindow.pack();
theWindow.setLocationByPlatform(true);
theWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
theWindow.setVisible(true);
}
}
There were other improvements I could not be bothered documenting (code speaks louder than words). If you have any questions (check the docs first, then) ask.
your issues are
1) by calling Thread.sleep(int) in the Swing related code, never do that, for delaying in Swing (there are lots of topics about why not use sleep in programing languages ...) use Swing Timer
2) your JPanel doesn't returns any XxxSize
3) for Swing use paintComponent(), only if you have got really important reasons then use method paint() more about repaint and animating Graphics in the 2D Graphics tutorial
4) Swing GUI should be built in the Event Dispatch Thread

Timer Flickers When It Reaches 0

importing libraries
public class Countdown1 extends Applet implements Runnable {
// getting user input
String input = JOptionPane.showInputDialog("Enter seconds: ");
// converting string to integer
int counter = Integer.parseInt(input);
Thread countdown;
public void start() {
countdown = new Thread(this);
countdown.start();
}
// executed by thread
public void run() {
Timer timer;
timer = new Timer(1000, new ActionListener() /* counting down time inputted */ {
public void actionPerformed(ActionEvent evt) {
if (counter > 0) {
counter--;
// repainting each second
repaint();
}
}
});
// timer started
timer.start();
}
public void paint(Graphics g) {
//painting text and time
g.setFont(new Font("Times New Roman", Font.BOLD, 35));
g.drawString("Seconds: " + String.valueOf(counter), 260, 210);
setBackground(Color.orange);
setForeground(Color.magenta);
// change background to cyan when timer reaches 0
if (counter == 0) {
setBackground(Color.cyan);
}
}
}
The problem isn't your Timer (although I do question the need to start it within a separate thread), the problem is with overriding paint.
Top level containers like Applet are not double buffered, meaning that the each paint action tends to be sent to the underlying Graphics device desperately.
Now you can over come this by employee some double buffering process OR you could...
Extend from JApplet instead
Create a custom component extending from something like JPanel and override it's paintComponent method instead, moving your custom painting to this method
Add this component to the applet instead.
It should solve the immediate issue...
You should also avoid calling setForeground or setBackground from within any paint method. In fact, you should avoid calling any method that might call repaint from within any paint method...
Take a look at Performing Custom Painting
I'm pretty sure that String input = JOptionPane.showInputDialog("Enter seconds: "); this is a bad idea. Instead, you should provide some kind of control or option within the UI to change this value...
Crude example
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Countdown1 extends JApplet {
#Override
public void init() {
add(new CounterPane());
}
public class CounterPane extends JPanel {
String input = JOptionPane.showInputDialog("Enter seconds: ");
int counter = Integer.parseInt(input);
public CounterPane() {
Timer timer;
timer = new Timer(1000, new ActionListener() /* counting down time inputted */ {
public void actionPerformed(ActionEvent evt) {
System.out.println(counter);
if (counter > 0) {
counter--;
setBackground(Color.orange);
setForeground(Color.magenta);
if (counter <= 0) {
setBackground(Color.cyan);
}
repaint();
}
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(new Font("Times New Roman", Font.BOLD, 35));
g.setColor(getForeground());
g.drawString("Seconds: " + String.valueOf(counter), 260, 210);
}
}
}

Categories

Resources