Java Applet Thread Animation - java

I am gone through some of code java applet and animation, i write the following code :
import java.applet.*;
import java.awt.*;
/*<applet code="AppletDemo" width = 200 height = 100></applet>
*/
public class AppletDemo extends Applet implements Runnable
{
String msg = "Text Animating from right to left...";
Thread t = null;
int state;
boolean stopFlag;
int msgX = 200;
String s;
boolean diff;
public void init()
{
setBackground(Color.cyan);
setForeground(Color.black);
}
public void start()
{
t = new Thread(this);
stopFlag = false;
t.start();
s = "abc";
diff = s.equalsIgnoreCase("abc");
}
public void run()
{
while (true)
{
try{
if(msgX>=-150)
msgX--;
else
msgX =200;
Thread.sleep(10);
repaint();
}
catch(Exception e)
{}
}
}
public void paint(Graphics g)
{
g.drawString(msg,msgX,20);
showStatus(diff+"Text at "+msgX+",20");
}
}
What is happening is that when i put Thread.sleep(100), it works fine but when i try to animate faster that is Thread.sleep(10) it starts flickering , i couldn't understand what is happening can anyone help.

Don't directly paint over top level container. Use JPanel to paint on it.
Don't use Thread.sleep(). It's better to use Swing Timer for animation.
Override paintComponent() method of JPanel for custom painting.
Don't forget to call super.paintComponent() inside overridden paintComponent method.
Instead of infinite loop try with Swing Timer.
Please have a look at How to Use Swing Timers
sample code:
import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
/*
* <applet code="AppletDemo" width = 200 height = 100></applet>
*/
public class AppletDemo extends Applet {
String msg = "Text Animating from right to left...";
int state;
boolean stopFlag;
int msgX = 200;
String s;
boolean diff;
JPanel panel;
public void init() {
setBackground(Color.cyan);
setForeground(Color.black);
panel = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponents(g);
g.drawString(msg, msgX, 20);
showStatus(diff + "Text at " + msgX + ",20");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 40);
}
};
add(panel);
int delay = 10; // milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (msgX >= -150)
msgX--;
else
msgX = 200;
repaint();
}
};
Timer timer = new Timer(delay, taskPerformer);
timer.setRepeats(true);
timer.start();
}
}
Find a Sample code here

Yes it will flicker. You will have to solve this problem using the concept of DoubleBuffering. It means that the image to be drawn is already buffered before its drawn on screen. It will remove the flickering effect.

Related

Ball doesn't move; Thread?

This is an UI that makes a ball go down in a diagonal way, but the ball stays static; it seems something is not working adecuatedly with the threads. Could you please, tell me how to make the ball move?
Please download a ball and change the directory so the program can find where your ball is allocated. It's not necessary to download the soccer pitch but if you want, it's OK. Finally, I have to thank you for spending time in search of this malfunctioning.
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import java.io.File;
class Animation extends JFrame implements ActionListener { //Frame and listener
Rectangle2D dimensions = new Rectangle2D.Double(0,0,850,595); //Not implemented limits
JButton animate, stop;
Runnable runnable;
Thread move;
public Animation() {
setLayout(new BorderLayout()); //BorderLayout disposition
setTitle("Pelota en acción");
animate = new JButton("Animate it!"); //Button to create balls
animate.setBounds(0,0,120,30);
animate.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
Image ball = null;
new Layout().createEllipse(ball);
runnable = new Layout();
move = new Thread(runnable);
move.start();
}
});
stop = new JButton("Freeze"); //Button to interrupt thread (not implemented)
stop.setBounds(0,0,120,30);
stop.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
move.interrupt();
Layout.running = false;
}
});
JPanel subPanel = new JPanel(); //Layout with its buttons situated to the south
subPanel.add(animate);
subPanel.add(stop);
add(subPanel,BorderLayout.SOUTH);
add(new Layout());
}
public static void main(String[] args) {
Animation ventana = new Animation();
ventana.setSize(850,625);
ventana.setLocationRelativeTo(null);
ventana.setVisible(true);
ventana.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent e) {} //Tag
} //Class close
class Layout extends JPanel implements Runnable { //Layout and thread
int X,Y; //Coordenadas
static boolean running = true; //"To interrupt the thread" momentaneously.
static ArrayList<Image> balls = new ArrayList<>(); //Balls collection
#Override
public void run () { //Just moves ball towards Narnia xd
while(running) {
X++; Y++;
System.out.println(X+" "+Y);
repaint();
updateUI();
try {
Thread.sleep(4);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
repaint();
updateUI();
try {
URL url = new URL("https://www.freejpg.com.ar/image-900/9c/9ca2/F100004898-textura_pasto_verde_linea_de_cal.jpg");
Image picture = ImageIO.read(url);
g.drawImage(picture,0,0,null);
} catch(IOException e){
System.out.println("URL image was not found");
}
finally {
try {
//----------------------------------------------------------------------------
Image picture = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Cancha.jpg")); //Pitch
//----------------------------------------------------------------------------
g.drawImage(picture, 0, 0, null);
} catch (IOException ex) {
System.out.println("Pitch image was not found");
}
}
for (Image ball : balls) { //I add balls to the Layout
g2.drawImage(ball,X,Y,100,100,null);
}
}
public void createEllipse (Image ball) { //Method that adds balls to the collection
try {
//-------------------------------------------------------------------- Ball
ball = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Pelota.png")); //Change this
//-------------------------------------------------------------------- Ball
} catch(IOException ex) {
System.out.println("Any balls were found");
}
balls.add(ball);
}
}
So to break your code down:
When the button is pressed, you execute the following code:
Image ball = null;
new Layout().createEllipse(ball);
runnable = new Layout();
move = new Thread(runnable);
move.start();
This will create a new layout. The run() method of this will increase the X and Y variables. They are declared here:
int X,Y; //Coordenadas
Those are instance variables, this means they belong to your newly created Layout.
Then you call repaint() on the new Layout, which will do nothing, because this new Layout has not been added to some window.
So, how do you fix this?
First, you have to keep the original Layout around:
class Animation extends JFrame { // no need to implement ActionListener
Rectangle2D dimensions = new Rectangle2D.Double(0,0,850,595); //Not implemented limits
JButton animate, stop;
Thread move;
Layout layout;
Then remember the Layout when you create it:
// before: add(new Layout());
layout = new Layout();
add(layout);
Then use the layout in your ActionListener:
layout.createEllipse(ball);
move = new Thread(layout);
move.start();
This might have some problems with concurrency (Swing is not thread-safe), so for good measure, you should call repaint() in the AWTEventThread:
// in run(), was repaint():
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
repaint();
}
});
Now, there are some cleanup tasks left:
Delete this code:
#Override
public void actionPerformed(ActionEvent e) {} //Tag
It's no longer needed, because you don't implement ActionListener.
Drop the static modifiers from some fields, and add volatile:
volatile int X,Y; //Coordenadas
volatile boolean running = true; //"To interrupt the thread" momentaneously.
ArrayList<Image> balls = new ArrayList<>(); //Balls collection
volatile is needed for variables that are accessed from more than one thread.
Also remove repaint() and resetUI() from the paint method. You don't need them.
For the pictures in paint: you should cache them. Store them in a field, so you don't have to load the picture every time.
When all this is done, your code is much cleaner, but there are still some warts that should be addressed. But at least you have something working.
Johannes has already spoken about many of the things which are wrong with your original example, so I won't go over many of them again.
This example makes use of a Swing Timer instead of a Thread as the main "animation" loop. It also focuses on demonstrating encapsulation and responsibility.
For example, the AnimtionPane is responsible for managing the balls, managing the animation loop and paint. It isn't, however, responsible for determining "how" the balls are updated or paint, it only provides the timing and functionality to make those things happen.
A couple of the glaring issues I can see are:
Trying to load resources from within the paintComponent method. This is a bad ideas, as it could slow you paint pass down, causing your UI to lag
Calling repaint and updateUI from within the paintComponent method. You should avoid causing any new updates to the UI from occurring during a paint process. This could cause your program to run wide and consume all the CPU cycles, not only making your app non-responsive, but also the whole system.
Some very quick points
Swing is not thread safe. You should never update the UI (or anything the UI relies on) from outside the context of the Event Dispatching Thread. This example uses a Swing Timer as it allows the delay to occur of the EDT (and not block the UI), but it's updates are triggered within the EDT, allowing us to safely update the UI from within
You create multiple instances of Layout, meaning that the one on the screen isn't the one which is been updated
Your "freeze" logic is broken. It will never "freeze" anything
Runnable example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private AnimationPane animationPane;
public TestPane() {
setLayout(new BorderLayout());
animationPane = new AnimationPane();
JButton actionButton = new JButton("Start");
actionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (animationPane.isAnimating()) {
animationPane.stop();
actionButton.setText("Start");
} else {
animationPane.start();
actionButton.setText("Stop");
}
}
});
add(animationPane);
add(actionButton, BorderLayout.SOUTH);
}
}
// This is just makes it seem more random ;)
private static Random RANDOM = new Random();
public class Ball {
private int x;
private int y;
private int xDelta;
private int yDelta;
private Color color;
private Shape shape;
public Ball(Color color) {
shape = new Ellipse2D.Double(0, 0, 10, 10);
this.color = color;
// Get some random motion
do {
xDelta = RANDOM.nextInt(6) + 2;
yDelta = RANDOM.nextInt(6) + 2;
} while (xDelta == yDelta);
}
public void update(Rectangle bounds) {
x += xDelta;
y += yDelta;
if (x + 10 > bounds.x + bounds.width) {
x = bounds.x + bounds.width - 10;
xDelta *= -1;
} else if (x < bounds.x) {
x = bounds.x;
xDelta *= -1;
}
if (y + 10 > bounds.y + bounds.height) {
y = bounds.y + bounds.height - 10;
yDelta *= -1;
} else if (y < bounds.y) {
y = bounds.y;
yDelta *= -1;
}
}
public void paint(Graphics2D g2d) {
// This makes it easier to restore the graphics context
// back to it's original state
Graphics2D copy = (Graphics2D) g2d.create();
copy.setColor(color);
copy.translate(x, y);
copy.fill(shape);
// Don't need the copy any more, get rid of it
copy.dispose();
}
}
public class AnimationPane extends JPanel {
// This does not need to be static
private List<Ball> balls = new ArrayList<>(); //Balls collection
private Timer timer;
private List<Color> colors;
public AnimationPane() {
colors = new ArrayList<>(8);
colors.add(Color.RED);
colors.add(Color.GREEN);
colors.add(Color.BLUE);
colors.add(Color.CYAN);
colors.add(Color.MAGENTA);
colors.add(Color.ORANGE);
colors.add(Color.PINK);
colors.add(Color.YELLOW);
timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
if (RANDOM.nextBoolean()) {
makeBall();
}
Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
for (Ball ball : balls) {
ball.update(bounds);
}
repaint();
}
});
makeBall();
}
protected void makeBall() {
Collections.shuffle(colors);
balls.add(new Ball(colors.get(0)));
}
public boolean isAnimating() {
return timer.isRunning();
}
public void start() {
timer.start();
}
public void stop() {
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
// Bad ideas. Repaint will cause a new paint event to be posted, causing your
// UI to run away - consuming all your CPU cycles in a singulator forms
// and destorys the known universe
//repaint();
// This doesn't do what you think it does and there shouldn't be
// reason for you to call it
//updateUI();
// This is a bad idea as it could cause the paint cycles to slow down
// destorying the responsiveness of your app
// Besids, you should be passing this as the ImageObserver
// try {
// URL url = new URL("https://www.freejpg.com.ar/image-900/9c/9ca2/F100004898-textura_pasto_verde_linea_de_cal.jpg");
// Image picture = ImageIO.read(url);
// g.drawImage(picture, 0, 0, null);
// } catch (IOException e) {
// System.out.println("URL image was not found");
// } finally {
// try {
// //----------------------------------------------------------------------------
// Image picture = ImageIO.read(new File("C:\\Users\\Home\\Desktop\\Cancha.jpg")); //Pitch
// //----------------------------------------------------------------------------
// g.drawImage(picture, 0, 0, null);
// } catch (IOException ex) {
// System.out.println("Pitch image was not found");
// }
// }
// This is "bad" per say, but each ball should have it's own
// concept of location
// for (Image ball : balls) { //I add balls to the Layout
// g2.drawImage(ball, X, Y, 100, 100, null);
// }
for (Ball ball : balls) {
ball.paint(g2);
}
// I made a copy of the graphics context, as this is shared
// with all the other components been painted, changing the
// render hints could cause issues
g2.dispose();
}
}
}

Using the repaint() method in a JPanel without erasing what is already drawn

I originally made the program as follows. I used extends Canvas and the update method to continually draw more points. My understanding is that every time repaint() is called the new points are added onto the existing Canvas through the use of the update() method. If the method was paint() instead of update() every time repaint() was called it would paint a new Canvas with only fifty points. This is my BarnsleyFern class which extends Canvas.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Canvas;
import java.lang.Math;
import java.awt.event.*;
import javax.swing.*;
public class BarnsleyFern extends Canvas
{
private double newX,x=0;
private double newY,y=0;
public BarnsleyFern(int width, int height)
{
setBackground(Color.BLACK);
setSize(width,height);
setVisible(true);
ActionListener action = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
repaint();
}
};
Timer timer = new Timer(100,action);
timer.start();
}
public void update(Graphics window)
{
Graphics2D g2d = (Graphics2D)window;
window.translate(360,800);
//g2d.rotate(Math.toRadians(180));
fern(window);
}
public void fern(Graphics window)
{
Color newColor = new Color((int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256));
for(int i=0;i<50;i++)
{
window.setColor(Color.BLUE);
int rand = (int)(Math.random()*100);
if(rand<1)
{
newX=0;
newY=0.16*y;
}
else if(rand<86)
{
newX=0.85*x + 0.04*y;
newY=0.85*y - 0.04*x + 1.6;
}
else if(rand<93)
{
newX=0.20*x - 0.26*y;
newY=0.23*x + 0.22*y + 1.6;
}
else
{
newX=0.28*y - 0.15*x;
newY=0.26*x + 0.24*y + 0.44;
}
window.fillOval((int)(newX*165.364),-(int)(newY*80.014),2,2);
x=newX;
y=newY;
}
}
}
The following code is the BarnsleyFernRunner class which extends JFrame. This sets up the JFrame and contains the main method.
import javax.swing.JFrame;
public class BarnsleyFernRunner extends JFrame
{
public BarnsleyFernRunner()
{
setTitle("Barnsley Fern");
setSize(800,800);
add(new BarnsleyFern(800,800));
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args)
{
BarnsleyFernRunner runner = new BarnsleyFernRunner();
}
}
I then decided to change my code a little by extending JPanel instead of extending Canvas. I choose to do this because the JPanel and JFrame are both from the javax.swing package. The new BarnsleyFern class uses the BarsnleyFernRunner class from above. This class does not work the same way as it did before. Each time repaint() is called fifty new points are draw and the old ones disappear. My understanding is that this is the case because every time repaint() is called a new JPanel is made. My question is how can I call the repaint() method within the JPanel without creating a whole new JPanel. Is there a way to call repaint() without "erasing" what I already have drawn. Is there a way I can duplicate what I did in the first class with the update() method. This is my BarnsleyFern class which extends JPanel
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Canvas;
import java.lang.Math;
import java.awt.event.*;
import javax.swing.*;
public class BarnsleyFern extends JPanel
{
private double newX,x=0;
private double newY,y=0;
public BarnsleyFern(int width, int height)
{
setBackground(Color.BLACK);
setSize(width,height);
setVisible(true);
ActionListener action = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
repaint();
}
};
Timer timer = new Timer(100,action);
timer.start();
}
public void paintComponent(Graphics window)
{
super.paintComponent(window);
Graphics2D g2d = (Graphics2D)window;
window.translate(360,800);
//g2d.rotate(Math.toRadians(180));
fern(window);
}
public void fern(Graphics window)
{
Color newColor = new Color((int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256));
for(int i=0;i<50;i++)
{
window.setColor(Color.BLUE);
int rand = (int)(Math.random()*100);
if(rand<1)
{
newX=0;
newY=0.16*y;
}
else if(rand<86)
{
newX=0.85*x + 0.04*y;
newY=0.85*y - 0.04*x + 1.6;
}
else if(rand<93)
{
newX=0.20*x - 0.26*y;
newY=0.23*x + 0.22*y + 1.6;
}
else
{
newX=0.28*y - 0.15*x;
newY=0.26*x + 0.24*y + 0.44;
}
window.fillOval((int)(newX*165.364),-(int)(newY*80.014),2,2);
x=newX;
y=newY;
}
}
}
When you call super.paintComponent(window); in your paintComponent() method, you are asking JPanel to paint your border and background. Painting a background means clobbering whatever was there before.
The simplest way to deal with this is to simply omit the call to super.paintComponent(window);. Another way is to call setOpaque(false);. This tells the panel that you are responsible for drawing the background yourself.

Java applet repaint a moving circle

I've just moved over from Pygame so Java 2D in an applet is a little new to me, especially when it comes to repainting the screen. In pygame you can simply do display.fill([1,1,1]) but how do I do this in an applet in Java? I understand the use of repaint() but that doesn't clear the screen - any moving object is not 'removed' from the screen so you just get a long line of painted circles.
Here's my code that I've been testing with:
package circles;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
public class circles extends Applet implements Runnable {
private static final long serialVersionUID = -6945236773451552299L;
static Random r = new Random();
String msg = "Click to play!";
static int w = 800, h = 800;
int[] txtPos = { (w/2)-50,(h/2)-50 };
int[] radiusRange = { 5,25 };
int[] circles;
static int[] posRange;
int x = 0, y = 0;
int radius = 0;
int cursorRadius = 10;
boolean game = false;
public static int[] pos() {
int side = r.nextInt(5-1)+1;
switch(side) {
case 1:
posRange = new int[]{ 1,r.nextInt(w),r.nextInt((h+40)-h)+h,r.nextInt(270-90)+90 };
break;
case 2:
posRange = new int[]{ 2,r.nextInt((w+40)-w)+w,r.nextInt(h),r.nextInt(270-90)+90 };
break;
case 3:
posRange = new int[]{ 3,r.nextInt(w),r.nextInt(40)-40,r.nextInt(180) };
break;
case 4:
posRange = new int[]{ 4,r.nextInt(40)-40,r.nextInt(h),r.nextInt(180) };
break;
}
System.out.println(side);
return posRange;
}
public void start() {
setSize(500,500);
setBackground(Color.BLACK);
new Thread(this).start();
}
public void run() {
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics e) {
Graphics2D g = (Graphics2D) e;
if(System.currentTimeMillis()%113==0) {
x+=1;
y+=1;
}
g.setColor(Color.BLUE);
g.fillOval(x,y,20,20);
repaint();
}
}
You need to call super.paint(g); in your paint method, as to not leave paint artifacts.
Never call repaint() from inside the paint method
Don't explicitly call paint, as you do in update(), when you mean to call reapaint()
just update the x and y values from inside the update() method, then call repaint()
You don't need to take a Graphics argument in update()
You need to call update() somewhere repeatedly in a loop, as it updates the x and y and reapint()s
If your class is going to be a Runnable, then you should put some code in the run() method. That's probably where you should have your loop
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class circles extends Applet implements Runnable {
int x = 0, y = 0;
public void start() {
setSize(500, 500);
setBackground(Color.BLACK);
new Thread(this).start();
}
public void run() {
while (true) {
try {
update();
Thread.sleep(50);
} catch (InterruptedException ex) {
}
}
}
public void update() {
x += 5;
y += 6;
repaint();
}
public void paint(Graphics e) {
super.paint(e);
Graphics2D g = (Graphics2D) e;
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
}
Side Notes
Why use Applets in the first place. If you must, why use AWT Applet and not Swing JApplet? Time for an upgrade.
Here's how I'd redo the whole thing in Swing, using a Swing Timer instead of a loop and Thread.sleep, as you should be doing.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Circle extends JPanel{
private static final int D_W = 500;
private static final int D_H = 500;
int x = 0;
int y = 0;
public Circle() {
setBackground(Color.BLACK);
Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new Circle());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
See How to use Swing Timers
See Create GUIs with Swing
Here's more advanced example for you to look at and ponder.
UPDATE
"Problem is, that's a JPANEL application. I specifically want to make an applet easily usable on a web page. "
You can still use it. Just use the JPanel. Take out the main method, and instead of Applet, use a JApplet and just add the JPanel to your applet. Easy as that.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CircleApplet extends JApplet {
#Override
public void init() {
add(new Circle());
}
public class Circle extends JPanel {
private static final int D_W = 500;
private static final int D_H = 500;
int x = 0;
int y = 0;
public Circle() {
setBackground(Color.BLACK);
Timer timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
}
}

Animate bg color with hsb model Java

I'm fairly new to java, so i don't think I have this fairly close to right, but I can seem to find any other help with this. Basically, I'm trying to animate a jPanel's background color so that it's hue (I'm using an hsb color model) changes. sort of like this: https://kahoot.it/#/ notice how the color kinda floats from one to another. Here is my code that I have so far:
public void animate(){
for(float i=.001f;i<1f;i+=.001f){
jPanel1.setBackground(Color.getHSBColor(i, .53f, .97f));
try{
Thread.sleep(5L);
}catch(InterruptedException ex){
}
System.out.println(i);
}
}
now i know this probably isn't right, but the loop works fine, the only problem is that the jPanel doesn't "update" until the loop is finished. Sorry all for being a huge noob at stuff like this, and thanks for any responses
The problem is that you are blocking the event dispatch thread, so no drawing can happen. Use a swing Timer instead of sleeping. A running example of changing HSB colors:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ColorCycle {
private static class ColorPanel extends JPanel {
private final float stepSize;
private final Timer timer;
private int index;
ColorPanel(final int steps, int fps) {
stepSize = 1f / steps;
timer = new Timer(1000 / fps, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
index++;
if (index > steps) {
index = 0;
}
repaint();
}
});
}
void start() {
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.getHSBColor(index * stepSize, 1f, 1f));
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Colors");
ColorPanel panel = new ColorPanel(300, 20);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
panel.start();
}
});
}
}
Works for me...
import java.awt.Color;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class HsbBackground {
public static void main(String[] args) throws Exception {
new HsbBackground();
}
private JPanel jPanel1 = new JPanel();
public HsbBackground() throws InvocationTargetException, InterruptedException {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
JFrame jFrame = new JFrame();
jFrame.setContentPane(jPanel1);
jFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jFrame.setSize(400, 300);
jFrame.setVisible(true);
}
});
animate();
}
public void animate() {
for (float i = .001f; i < 1f; i += .001f) {
final float j = i;
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jPanel1.setBackground(Color.getHSBColor(j, .53f, .97f));
}
});
try {
Thread.sleep(5L);
} catch (InterruptedException ex) {
}
System.out.println(i);
}
}
}
Make sure, that you call setVisible of your frame before you start your loop.
PS: I updated the code, so that GUI changes are all made from the event dispatching thread. The code still works fine.

Custom JComponent(s) refuse to paint (if added to an ArrayList)

here's the relevant code from the form where JComponents should be drawn - this code creates and adds to the form a JComponent and also adds it to an ArrayList, as I need to be able to destroy the components later.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;
public class frmMain extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
/**
* Creates new form frmMain
*/
public frmMain() {
initComponents();
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
// Pocitadlo tmp = new Pocitadlo(100, 40, 40, getFont());
// tmp.addActionListener(this);
// this.add(tmp);
// tmp.start();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int val, x, y;
val = 20;
x = (this.getWidth() / 2) - 10 + rnd.nextInt(20);
y = (this.getHeight() / 2) - 10 + rnd.nextInt(20);
try{
val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(this);
poc.add(tmp);
tmp.setVisible(true);
tmp.start();
this.add(tmp);
this.setTitle("Počet počítadiel: " + this.getComponentCount());
repaint();
tmp.repaint();
} catch(Exception e){
JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
}
}
#Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("counterFinished")){
Pocitadlo tmp = (Pocitadlo)e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
this.remove(tmp);
}
}
and here is the code of the JComponents i'm adding and naiively expect to be drawn:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.Timer;
public class Pocitadlo extends JComponent implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f){
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
public void start(){
isRunning = true;
valueTimer.start();
}
public void stop(){
isRunning = false;
valueTimer.stop();
}
#Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("valueTimer")){
value--;
if(actionListener != null){
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if(value == 0 && actionListener != null){
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
}
if(value < maxValue / 2 && !blinkTimer.isRunning()) blinkTimer.start();
}
if(e.getActionCommand().equals("blinkTimer")){
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener){
actionListener = listener;
}
#Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if(value > maxValue) value = maxValue;
repaint();
}
#Override
public void paintComponent(Graphics g){
g.setColor(Color.red);
g.fillRect(getX(), getY(), getWidth(), getHeight());
if(isDisplayed){
g.setColor(Color.green);
g.fillRect(getX(), getY(), getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
g.drawString(value + "/" + maxValue, 0, 0);
}
//super.paintComponent(g);
}
}
this results in nothing - the JComponents are created and correctly added at least to the ArrayList, and its paintComponent method run, as well as the timer events, components are also correctly removed at least from the ArrayList when the countdown reaches 0, but doesn't draw anything and I have no idea why.
please note that the commented code in frmMain constructor kind of works, and if uncommented, it draws the JComponent... well... just the rectangles, doesn't draw any text, but I suppose that's another problem, however, if you're willing to help with that one too, I'd be glad.
also, if I'm using action events and listeners incorrectly, please ignore that, I'm a beginner at Java and would like to know how to do it correctly, but later, right now I just need to get this code to work so please focus on the issue, thank you.
yes, there's some dead things, like the font passed to the component, those are remains of my attempts to get the drawString to work.
You project is suffering from a number of really basic misconceptions about how painting in Swing works.
Because (at the time I tested it) you hadn't provided the full code, I had to make some assumptions.
Your expection is that the content pane of the JFrame is using a null layout. I'd find it difficult to see how you would mange this and have other components on the frame. In any case, I would isolate a separate container to act as the "main" view for your Pocitadlo. This allows you to continue using layout managers to manage the other components as well.
I'm a little concerned that you trap the entire Pocitadlo generation code in a try-catch simply to catch a numeric conversion. Since you've already applied a default value, it would be reasonable to trap the conversion in its own try-catch and ignore it if it fails (displaying a message as required). Now I'm coming in from the outside, so you may see a need for this, it just caught my eye...
Your valueTimer actionPerformed code is slightly erronous, as it's possible for the blinkTimer to continue running even after the valueTimer has completed. I corrected this by merging two of you if statements into a if-else statement instead...
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
I, personally, would use a JPanel over a JComponent. JPanel is opaque to start with.
The main problem is in you paintComponent method.
You should always call super.paintComponent, and on opaque components it should be called first.
You seem to think you have to correct for the components position when painting, for example g.fillRect(getX(), getY(), getWidth(), getHeight());. This is actually wrong. The graphics context has already been translated so that the x/y position of the component/graphics is now equal to 0x0. Have a read through Painting in AWT and Swing for a better explanation, but basically, this means, the top/left corner of you paint area is now 0x0.
You seem to think that text is painted from the top/left corner of the first character. This is actually wrong. Text is painted along it's base line. You need to compenstate for this by adjusting the y-position by the Fonts ascent value.
.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Random;
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 TestRepaint01 {
public static void main(String[] args) {
new TestRepaint01();
}
public TestRepaint01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainFrame extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
private JPanel body;
/**
* Creates new form frmMain
*/
public MainFrame() {
setLayout(new BorderLayout());
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
body = new JPanel(null);
add(body);
JButton btn = new JButton("Add");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int val, x, y;
val = 20;
x = (getWidth() / 2) - 10 + rnd.nextInt(20);
y = (getHeight() / 2) - 10 + rnd.nextInt(20);
// try {
// val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(MainFrame.this);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
poc.add(tmp);
tmp.start();
body.add(tmp);
body.repaint();
setTitle("Počet počítadiel: " + getComponentCount());
// } catch (Exception e) {
// JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
// }
}
});
add(btn, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("counterFinished")) {
System.out.println("Counter finished");
Pocitadlo tmp = (Pocitadlo) e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
body.remove(tmp);
// body.revalidate();
body.repaint();
}
}
}
public class Pocitadlo extends JPanel implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f) {
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
#Override
public void removeNotify() {
super.removeNotify();
stop();
blinkTimer.stop();
}
public void start() {
isRunning = true;
valueTimer.start();
}
public void stop() {
isRunning = false;
valueTimer.stop();
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("valueTimer")) {
value--;
System.out.println("value = " + value);
if (actionListener != null) {
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
}
if (e.getActionCommand().equals("blinkTimer")) {
System.out.println("Blink");
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener) {
actionListener = listener;
}
#Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if (value > maxValue) {
value = maxValue;
}
repaint();
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
// location bad
g.fillRect(0, 0, getWidth(), getHeight());
if (isDisplayed) {
g.setColor(Color.green);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
FontMetrics fm = g.getFontMetrics();
g.drawString(value + "/" + maxValue, 0, fm.getAscent());
}
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
}
}
None of the problems you've encountered are super critical (sure, you program doesn't do what you want) and suggest that you've reached a pinnacle moment in your code abilities. I say this, because I made the exact same mistakes... ;)
Take a closer look at 2D Graphics and Custom Painting
I'd also take a look at Code Conventions for the Java Programming Language as you simply annoy people if you don't follow them ;)

Categories

Resources