Delay is not working in java graphics - java

This is a code for drawing points on calculated locations by Bresenham's algorithm:
public void drawBresenhamPoints(Graphics2D g2, List<Point> bresenham) throws InterruptedException
{
Graphics2D g = (Graphics2D) g2;
if(bresenham == null)
return;
g.setColor(Color.DARK_GRAY);
for(int i = 0; i < bresenham.size(); i = i+20)
{
int x = bresenham.get(i).x - pointWidth1/2;
int y = bresenham.get(i).y - pointWidth1/2;
int ovalW = pointWidth1;
int ovalH = pointWidth1;
g.fillOval(x, y, ovalW, ovalH);
// delay
try
{
Thread.sleep(10);
}
catch(Throwable e)
{
System.out.println(e.getMessage());
}
}
}
The list 'bresenham' contains all the points which are pre-calculated with the help of Bresenham's line drawing algorithm. I want to set a delay of 1 second inside the 'for' loop so that each and every point is drawn after an interval of 1 second. The portion listed in the 'delay' section doesn't work. How to make 'delay' work?
More specifically, I want to see all the points being drawn one by one on the screen in an interval of 1 second.

I'm assuming you're calling this method in a paint/paintComponent method.
Just a pointer: Never ever ever sleep the paint process
Instead use a javax.swing.Timer for repeated tasks. What I would do is
Have two Lists. Your List<Point> bresenham and another List<Point> paintList. The bresenham will hold your data, and the paintList will be initially empty.
Use the paintList to paint your points
#override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
for (Point p : paintList) {
int x = bresenham.get(i).x - pointWidth1/2;
int y = bresenham.get(i).y - pointWidth1/2;
int ovalW = pointWidth1;
int ovalH = pointWidth1;
g.fillOval(x, y, ovalW, ovalH);
}
}
Though there's nothing initially in the paintList, you will add a new Point to the list every firing of a timer event.
Timer timer = new Timer(100, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
if (bresenham.isEmpty()) {
((Timer)e.getSource()).stop();
} else {
paintList.add(bresemham.get(0));
bresenham.remove(0);
}
repaint();
}
});
timer.start();
The basic timer of the constructor is firs the delay, which is the time delayed between "iterations", and second argument in the listener that actually listens for the timer event that is fired every delay milliseconds. So what the code above basically does is add a Point to the paintList taken from the bresenham list, then removes the item the repaint which calls the paintComponent. When the list is empty, the timer will stop.
UPDATE
Here's a complete running example
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class BresenhamPoints extends JPanel {
private static final int D_W = 500;
private static final int D_H = 500;
private List<Point> bresenhamList;
private List<Point> paintList;
public BresenhamPoints() {
bresenhamList = createRandomPoints();
paintList = new ArrayList<>();
Timer timer = new Timer(100, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (bresenhamList.isEmpty()) {
((Timer) e.getSource()).stop();
} else {
paintList.add(bresenhamList.get(0));
bresenhamList.remove(0);
}
repaint();
}
});
timer.start();
}
private List<Point> createRandomPoints() {
Random rand = new Random();
List<Point> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add(new Point(rand.nextInt(D_H), rand.nextInt(D_H)));
}
return list;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Point p : paintList) {
g.fillOval(p.x - 5, p.y - 5, 10, 10);
}
}
#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 BresenhamPoints());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

The value for the sleep method is in milliseconds, so there you are sleeping for 10ms. Changing it to 1000 will create a more noticeable interrupt.
As pointed out, you should never have any time consuming or even worse locking mechanisms on the EDT since it will hang your entire application. You could use a Timer to fire off events and draw one point at a time. This previous SO post should do what you need.

Related

Trying to create disappearing squares in Java

class GraphicsExampleComponent extends JComponent
{
//#Override
public void paintComponent(Graphics g)
{
//make the first call to your recursive routine
drawSquare1(g, 0, 0, 80);
}
public void drawSquare1(Graphics g, int x, int y, int size)
{
//draw a rectangle
g.drawRect(x, y, size, size);
g.fillRect(x, y, size, size);
//reset the parameters
x = x + size + 10;
y = y + (size/4);
size = size/4;
//determine if you should call it again.
if (size<4 || x>600)
drawSquare1(g, x, y, size);
}
My assignment is to create disappearing squares that get 25% smaller as they move to the right. When I run the code, it just creates the one square and stops. Can anyone help me understand what I am doing wrong.
disappearing squares that get 25% smaller as they move to the right.
Ok, let's step back for a second and break this down a bit.
You need to know...
The amount of space to be covered
The amount of space already covered (by the square)
The original size of the square
The size of the square should be when it reaches the other side (25% smaller)
When you have all this, you can calculate the size of the square at any point along its journey.
To determine the amount of space, you can use the component's width, via getWidth().
To determine the space already covered, you could start by having a look at box's current x position
// Assuming that box is a instance of Rectangle
double progress = (double)box.x / (double)getWidth();
We could argue that we should look at the middle of the box, or the trailing edge, but both of those are easy to implement.
Next, we need know the range of change (from start to end size), we can then use that to calculate the delta to be applied to the box...
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
Soooo, this will provide with the means to determine the size of the component based on it's current location (horizontally) through the component.
Next, you need some way to update the box's position and update the UI.
One of the better solutions is to use a Swing Timer. This will allow you to perform a repeating action (with a specified delay between updates) which won't block the UI and will generate updates within the Event Dispatching Queue, which is important because Swing is not Thread safe.
Have a look at How to Use Swing Timers for more details.
And finally, all we need, is to update the component with current state, via it's paintComponent method ... easy :P
Runnable example
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 javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
protected static double startSize = 50;
protected static double endSize = startSize * 0.25;
private Rectangle box;
private Timer timer;
public TestPane() {
box = new Rectangle(0, 100 - 25, 50, 50);
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (box.x + box.width >= getWidth()) {
box.x = getWidth() - box.width;
box.width = (int)endSize;
box.height = (int)endSize;
timer.stop();
repaint();
}
box.x += 1;
double progress = (double)box.x / (double)getWidth();
double range = startSize - endSize;
double value = (range * progress);
box.width = (int)startSize - (int)value;
box.height = (int)startSize - (int)value;
repaint();
}
});
}
#Override
public void addNotify() {
super.addNotify();
box.x = 0;
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(box);
g2d.dispose();
}
}
}
The condition if (size < 4 || x>600) is never true because when drawSquare1 is invoked for the first time size=80 and x=0.
Changing it to say if (size > 4 && x<600) will paint 3 squares on the screen without any noticeable animation.
To animate it we'll need to add some delay between paintings and remove previously painted squares.
To do so we use a swing Timer. We use the timer to repeatedly invoke drawSquare1.
drawSquare1 should modify the parameters controlling the painting, and call repaint.
import java.awt.*;
import javax.swing.*;
public class Main {
Main() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
GraphicsExampleComponent board = new GraphicsExampleComponent();
frame.add(board);
frame.pack();
frame.setVisible(true);
board.animate();
}
public static void main(String[] args) {
new Main();
}
}
class GraphicsExampleComponent extends JComponent{
private final static int W = 200, H = 100, MIN_SIZE = 4, STEP = 10, DELAY = 2000;
private int x= 0, y = 0, size = 80;
private Timer timer;
void animate(){
timer = new Timer(DELAY, e->drawSquare());
timer.start();
}
public void drawSquare(){
//check stop criteria
if (size < MIN_SIZE || x >= getWidth()) {
timer.stop();
return;
}
//reset the parameters
x = x + size + STEP;
y = y + size/4;
size = size/4;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//draw a rectangle
g.fillRect(x, y, size, size);
g.dispose();;
}
#Override
public Dimension preferredSize() {
return new Dimension(W, H);
}
}
(Test in online here)
Thank you everyone for the help. I unfortunately could not use a timer due to us not learning about it in class and we are not allowed to use methods/codes that we haven't gone over. To fix this I had to fix my base case by making it size >= 4 && x < 600.

Scaling image through thread - JAVA

Let say I have an image. I put the image in a JPanel and add the JPanel inside a JFrame. The image moves from the bottom part of the frame to top of the frame while its size is also decreased using AffineTransform. The variable changes using thread.
So here's the following code:
public class SplashScreen extends JFrame{
Image img1;
int w=1,h=1;
int x=0,y=0;
Thread th = new Thread(new Runnable() {
#Override
public void run() {
while(true){
w-=0.05;
h-=0.05;
y-=2;
x+=1;
if(y==-100){
new MainMenu_BlueJay().setVisible(true);
dispose();
}
repaint();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Logger.getLogger(SplashScreen.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
JPanel p = new JPanel();
public SplashScreen(){
setLayout(new BorderLayout());
p.setPreferredSize(new Dimension(900,600));
p.setBackground(Color.black);
p.setLayout(new GridLayout());
add(p);
setTitle("BlueJay");
setSize(900,600);
getContentPane().setBackground(Color.black);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
th.start();
requestFocus();
setFocusable(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
img1 = new ImageIcon("images/Intro/BJ Production 2013.png").getImage();
AffineTransform at = new AffineTransform();
at.scale(w,h);
g2d.setTransform(at);
g2d.drawImage(img1, x, y, p);
}
public static void main(String[] args) {
new SplashScreen();
}
However what I get from code above is only black screen. What's the matter? Anyway, If I don't use the AffineTransform function (just move it from bottom to top), the image is shown and moves BUT the frame is flickered (blinking) rapidly.
Any idea to solve this problem so I could move the image while decrease its size and also solve the flickered/rapid blinking frame?
You should not override the paint method of the JFrame. If you want to paint anything, you should paint this in a class that extends JPanel, where you override the paintComponent method.
You should NOT load the image in the painting method. This is horribly inefficient. You should load the image only ONCE, probably in a constructor.
You should not call Graphics2D#setTransform(). Have a look at the JavaDoc at http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html#setTransform%28java.awt.geom.AffineTransform%29 , which explicitly states
WARNING: This method should never be used to apply a new coordinate transform on top of an existing transform
You should think about your w and h values. Should they be the size of the image that is painted, or used as scaling factors for the image? Setting them as the scaling factors of an AffineTransform will NOT have the effect of scaling an image to the desired size. At the moment, they are declared as int values, so something like w-=0.05 does not really make sense anyhow.
You should have a clear idea of how you are going to describe the animation that the image should perform.
One could possibly summarize this:
You should not write code and assume it is "correct" only because there are no compilation errors ;-)
However, the following snippet may be a first step towards your goal:
package stackoverflow;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SplashScreen extends JFrame
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new SplashScreen();
}
});
}
private PaintPanel paintPanel;
public SplashScreen()
{
setTitle("BlueJay");
setDefaultCloseOperation(EXIT_ON_CLOSE);
getContentPane().setBackground(Color.BLACK);
getContentPane().setLayout(new BorderLayout());
paintPanel = new PaintPanel();
getContentPane().add(paintPanel, BorderLayout.CENTER);
setSize(900,600);
setLocationRelativeTo(null);
setFocusable(true);
requestFocus();
setVisible(true);
startAnimation();
}
void startAnimation()
{
Thread thread = new Thread(new Runnable()
{
int x = 100;
int y = 100;
int w = 0;
int h = 0;
#Override
public void run()
{
try
{
Thread.sleep(500);
}
catch (InterruptedException ex)
{
Thread.currentThread().interrupt();
return;
}
while (true)
{
if (y == 200)
{
// new MainMenu_BlueJay().setVisible(true);
dispose();
}
x += 2;
y += 1;
w += 1;
h += 1;
paintPanel.setImageCoordinates(x, y, w, h);
repaint();
try
{
Thread.sleep(10);
}
catch (InterruptedException ex)
{
Thread.currentThread().interrupt();
return;
}
}
}
});
thread.start();
}
}
class PaintPanel extends JPanel
{
private final Image image;
private int imageX, imageY;
private int imageW, imageH;
PaintPanel()
{
image = new ImageIcon("Clipboard02.jpg").getImage();
imageX = 0;
imageY = 0;
imageW = 0;
imageH = 0;
}
void setImageCoordinates(int imageX, int imageY, int imageW, int imageH)
{
this.imageX = imageX;
this.imageY = imageY;
this.imageW = imageW;
this.imageH = imageH;
repaint();
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
float scalingX = (float) imageW / image.getWidth(null);
float scalingY = (float) imageH / image.getHeight(null);
g.scale(scalingX, scalingY);
int ix = (int)(imageX / scalingX);
int iy = (int)(imageY / scalingY);
g.drawImage(image, ix, iy, null);
}
}
Don't paint on top-level containers like JFrame. Instead use a JPanel and override its paintComponent method and call super.paintComponent
No need to call Thread.sleep(). Instead Use a javax.swing.Timer.
Run your program from the EDT
Don't create a new ImageIcon in the paint method. It will create new ImageIcon object every time repaint() is called. Instead, instantiate it in the constructor.
Here's a refactor of the code.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class SplashScreen extends JFrame {
Image img1;
int w = 900, h = 600;
int x = 0, y = 0;
public SplashScreen() {
setLayout(new BorderLayout());
add(new MyPanel());
setTitle("BlueJay");
setSize(900, 600);
getContentPane().setBackground(Color.black);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
requestFocus();
setFocusable(true);
}
private class MyPanel extends JPanel {
public MyPanel() {
img1 = new ImageIcon(SplashScreen.class.getResource("/resources/stackoverflow5.png")).getImage();
setBackground(Color.black);
setLayout(new GridLayout());
Timer timer = new Timer(20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
w -= 5;
h -= 5;
y -= 2;
x += 1;
if (y == -250) {
new MainMenu_BlueJay().setVisible(true);
dispose();
}
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
//AffineTransform at = new AffineTransform();
// at.scale(w, h);
// g2d.setTransform(at);
g2d.drawImage(img1, x, y, w, h, this);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(900, 600);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SplashScreen();
}
});
}
}
I'm not too familiar with Graphics2D so I commented out the AffirmTransformation stuff, but fixed your other problems.

How do you create multiple animated components with timer/actionevents with two different time intervals on the same panel?

I need help with a simple animation assignment. It goes as follows.
I have two stop lights on a JPanel and the object is for the two of them to have different time intervals i.e there lights cycle at different times.
Everything works fine if I only have one light at a time. I am relatively new to this but I believe I know the problem.
In the code under this text, I use this several times. I believe my issue occurs in the public void cycle() method in which it just says this.repaint(); I have a feeling that the panel is being repainted at the two different time periods and it gives me a somewhat random light changing instead of a nice cycle.
Is there a way I can have these two components on the same JPanel with a more specific repaint method (maybe a bounding box around the individual light fixtures) or would creating separate panels be a better option (and a little help if that is the case because I understand the basic layouts but have never used them before).
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class DrawingPanel extends JPanel implements Lighter
{
// instance variables
private final int INTERVAL1 = 2000;
private final int INTERVAL2 = 5000;
private TrafficLight _light1, _light2;
private LightTimer _timer1,_timer2;
/**
* Constructor for objects of class DrawingPanel
*/
public DrawingPanel()
{
// initialise instance variables
super();
this.setBackground(Color.CYAN);
_light1 = new TrafficLight(50,50);
_light2 = new TrafficLight(200,50);
_timer1 = new LightTimer(INTERVAL1,this);
_timer2 = new LightTimer(INTERVAL2,this);
_timer1.start();
_timer2.start();
}
public void cycle(){
_light1.cycle();
_light2.cycle();
this.repaint();
}
public void paintComponent(Graphics pen)
{
super.paintComponent(pen);
Graphics2D aBetterPen = (Graphics2D)pen;
_light1.fill(aBetterPen);
_light2.fill(aBetterPen);
}
}
Running two timers in the fashion is achievable. Personally, I would write a "signal" class that controls a single light with it's own timing and painting routines, but that's not what you've asked.
What you need to do is maintain some kind of state variable for each signal and update them separately.
You would then need to modify the paint code to detect these states and take appropriate actions...for 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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestBlink {
public static void main(String[] args) {
new TestBlink();
}
public TestBlink() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Timer blink1;
private Timer blink2;
private boolean light1 = false;
private boolean light2 = false;
public TestPane() {
blink1 = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
light1 = !light1;
repaint();
}
});
blink2 = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
light2 = !light2;
repaint();
}
});
blink1.start();
blink2.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = 20;
int x = (getWidth() - (radius * 2)) / 2;
int y = (getHeight() - (radius * 2)) / 2;
Ellipse2D signal1 = new Ellipse2D.Float(x, y, radius, radius);
Ellipse2D signal2 = new Ellipse2D.Float(x + radius, y, radius, radius);
g2d.setColor(Color.RED);
g2d.draw(signal1);
if (light1) {
g2d.fill(signal1);
}
g2d.setColor(Color.GREEN);
g2d.draw(signal2);
if (light2) {
g2d.fill(signal2);
}
g2d.dispose();
}
}
}
Updated
A better solution (as I stated earlier) would be to contain all the logic for a single sequence in a single class. This isolates the painting and allows you to change the individual nature each sequence.
For example...
This is a simple example which uses a fixed rate of change, so each light gets the same about of time...
public class TraficLight01 extends JPanel {
public static final int RADIUS = 20;
private Timer timer;
private int state = 0;
public TraficLight01() {
timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
state++;
if (state > 2) {
state = 0;
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(RADIUS, (RADIUS + 1) * 3);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = 20;
Ellipse2D light = new Ellipse2D.Float(0, 0, RADIUS, RADIUS);
int x = (getWidth() - radius) / 2;
int y = ((getHeight()- (radius * 3)) / 2) + (radius * 2);
Color color[] = new Color[]{Color.RED, Color.YELLOW, Color.GREEN};
for (int index = 0; index < color.length; index++) {
g2d.translate(x, y);
g2d.setColor(color[index]);
g2d.draw(light);
if (state == index) {
g2d.fill(light);
}
g2d.translate(-x, -y);
y -= radius + 1;
}
g2d.dispose();
}
}
Or you provide a variable interval for each light...
public static class TraficLight02 extends JPanel {
public static final int RADIUS = 20;
private Timer timer;
private int state = 0;
// Green, Yellow, Red
private int[] intervals = new int[]{3000, 500, 3000};
public TraficLight02() {
timer = new Timer(intervals[0], new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
timer.stop();
state++;
if (state > 2) {
state = 0;
}
timer.setInitialDelay(intervals[state]);
repaint();
timer.restart();
}
});
timer.start();
timer.setRepeats(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(RADIUS, (RADIUS + 1) * 3);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int radius = 20;
Ellipse2D light = new Ellipse2D.Float(0, 0, RADIUS, RADIUS);
int x = (getWidth() - radius) / 2;
int y = ((getHeight()- (radius * 3)) / 2) + (radius * 2);
Color color[] = new Color[]{Color.GREEN, Color.YELLOW, Color.RED};
for (int index = 0; index < color.length; index++) {
g2d.translate(x, y);
g2d.setColor(color[index]);
g2d.draw(light);
if (state == index) {
g2d.fill(light);
}
g2d.translate(-x, -y);
y -= radius + 1;
}
g2d.dispose();
}
}
It wouldn't take much to change these two concepts to seed them with variable intervals via a setter method.
Equally, you could use three Timers, each time one fires, you would simply start the next one. This way you could define a chain of timers, each one firing at different intervals after the completion of it's parent...
As a Note, your comment
it gives me a somewhat random light changing instead of a nice cycle.
What are you expecting it to look like?
With the Time intervals you have set it may appear somewhat random but it is actually working ie your intervals would work like this (well my assumptions of what the Interval variable are for)
Time(s) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Light1 ON OFF ON OFF ON OFF ON OFF
Light2 ON OFF ON

Is it possible to make a graphics object final for internal loop?

It seems like if my graphics object were final, as the error says it should be that I would never be able to change it. I have been reading about assigning variables to final variables before using them in my timer loop in order to get around this, but I don't even know how to begin to approach that for a graphics object. Would I need to copy the final graphics object back to the normal graphics object? Here is some code.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.util.ArrayList;
public class Test extends JPanel{
abstract class graphic {
public Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public int[] location = new int[] {screenSize.width/2,screenSize.height/2};
}
public class gladiator extends graphic {
void draw(Graphics g) {
g.setColor(Color.green);
g.fillArc(location[0], location[1], 100, 100, 45, 90);
g.setColor(Color.black);
g.fillArc((location[0]+50-10),(location[1]+50-10), 20, 20, 0, 360);
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
gladiator[] gladiator = new gladiator[2];
ArrayList<gladiator> gladiatorList = new ArrayList<gladiator>();
for (int a =0; a < 2; a++) {
final gladiator[a] = new gladiator();
final gladiatorList.add(gladiator[a]);
}
new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int a = 0; a < gladiatorList.size(); a++) {
gladiator[a].draw(g);
}
repaint();
System.out.println("repainting");
}
}).start();
}
public void setLocation(int x, int y){
//this.location[0] = x;
//this.location[1] = y;
}
public static void main(String[] args){
JFrame jf=new JFrame();
jf.setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
jf.setPreferredSize(Toolkit.getDefaultToolkit().getScreenSize());
jf.add(new Test());
jf.pack();
jf.setVisible(true);
}
}
This is the bit which returns that pretty much all of the line inside the for loop should be final.
new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (int a = 0; a < gladiatorList.size(); a++) {
gladiator[a].draw(g);
}
repaint();
System.out.println("repainting");
}
}).start();
Thanks!
To use a local variable inside an anonymous class defined in the same method, you must make the local variable final.
This doesn't prevent you from modifying the object to which a reference variable points.
In your case, your anonymous class is using g, gladiator and gladiatorList. So mark all these final:
protected void paintComponent( final Graphics g) {
...
final gladiator[] gladiator = new gladiator[2];
final ArrayList<gladiator> gladiatorList = new ArrayList<gladiator>();
You really shouldn't have the timer in your paintComponent, it fires off a new timer whenever the OS feels like painting the component. You can see this by simply changing your repainting sysout to System.out.println("repainting in: " + this);
As for the finality of the Graphics variable:
final Graphics2D g2d = (Graphics2D) g.create();
Use g2d inside the timer.
Edit:
A full example:
public class ExampleAnimationOfMyStuff extends JPanel {
MovingRectangle[] rectangles = new MovingRectangle[20];
public ExampleAnimationOfMyStuff() {
for (int i = 0; i < rectangles.length; i++) {
rectangles[i] = new MovingRectangle();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Animated rectangles");
ExampleAnimationOfMyStuff anime = new ExampleAnimationOfMyStuff();
frame.getContentPane().add(anime);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
anime.animate();
frame.setVisible(true);
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(1000, 1000);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (MovingRectangle rectangle : rectangles) {
g.setColor(rectangle.color);
g.fillRect(rectangle.x, rectangle.y, rectangle.width,
rectangle.height);
}
}
public void animate() {
new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (MovingRectangle rectangle : rectangles) {
rectangle.tick();
}
repaint();
System.out.println("repainting");
}
}).start();
}
public static class MovingRectangle extends Rectangle {
public static Random random = new Random();
int speedX, speedY;
Color color;
public void tick() {
if (getX() + speedX > 1000 || getX() + speedX < 0) {
speedX *= -1;
}
if (getY() + speedY > 1000 || getY() + speedY < 0) {
speedY *= -1;
}
setRect(getX() + speedX, getY() + speedY, getWidth(), getHeight());
}
public MovingRectangle() {
super(random.nextInt(1000), random.nextInt(1000), random
.nextInt(40), random.nextInt(40));
this.speedX = (random.nextDouble() > 0.5) ? 4 : -4;
this.speedY = (random.nextDouble() > 0.5) ? 4 : -4;
this.color = new Color(random.nextInt(256), random.nextInt(256),
random.nextInt(256));
}
}
}
The above code seperates the timer from the paintcomponent using an array of custom objects (like you have), no need to declare things final and also removes the flickering you experienced (due to new timers firing off). It paints pretty rectangles on the screen that move about ;)
If you make your variable final, it means that that variable will always be a reference to the same object instance. It does not mean that the contents of the object instance cannot be changed. You cannot assign a new reference to that variable, but you can do anything you want to the instance itself (call methods that might modify its state, read/write the fields, etc.).

paintComponent does not work if its called by the recursive function?

I want to see all the Points one after another but I see only able to see 1
point. What shold I change to see all the Points ?
In the System.out you can see 10 times "set" and then 2 times
"paintComponent". what should I change that after each time set is
called it change the "paintComponente" ?
==================================================================================
public class exampe extends JPanel
{
int x;
int y;
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(x-2,y-2,4,4);
System.out.println("paintComponent");
}
public void set(int X, int Y)
{
x = X;
y = Y;
System.out.println("set");
super.repaint();
}
public static void main(String args[])
{
int e=1;
JFrame frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
exampe ex= new exampe();
JScrollPane scroll = new JScrollPane(ex);
frame.getContentPane().add(scroll);
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
for(int i=0; i< 10; ++i)
ex.set(e+i,e+i);
}
}
*SIMPLE EXPLANATION AS TO WHY YOU COULD ONLY SEE THE LAST UPDATE : *
A quote taken from Filthy Rich Clients by Chet Haase and Romain Guy
It is important to note that repaint requests get “coalesced,” or combined.
So, for example, if you request a repaint and there is already one on the
queue that has not yet been serviced, then the second request is ignored
because your request for a repaint will already be fulfilled by the earlier
request. This behavior is particularly helpful in situations where many
repaint requests are being generated, perhaps by very different situations
and components, and Swing should avoid processing redundant requests and
wasting effort.
Try your hands on this, and ask what is not clear to you :
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class PointsExample
{
private CustomPanel contentPane;
private Timer timer;
private int x = 1;
private int y = 1;
private ActionListener timerAction = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
contentPane.set(x, y);
x++;
y++;
if (x == 450)
timer.stop();
}
};
/*
* This is just JFrame, that we be
* using as the Base for our Application.
* Though here we are calling our
* JPanel (CustomPanel), whose
* paintComponent(...) method, we had
* override.
*/
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("Locate Mouse Position");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
contentPane = new CustomPanel();
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
timer = new Timer(100, timerAction);
timer.start();
}
public static void main(String\u005B\u005D args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new PointsExample().createAndDisplayGUI();
}
});
}
}
class CustomPanel extends JComponent
{
private int x;
private int y;
public void set(int a, int b)
{
x = a;
y = b;
repaint();
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(500, 500));
}
#Override
public void paintComponent(Graphics g)
{
g.clearRect(0, 0, getWidth(), getHeight());
Graphics2D g2 =(Graphics2D) g;
g2.fillOval(x, y, 4, 4);
}
}
Here is the code, that will allow you to have a look at your points while iterating inside a for loop, though this approach is highly discouraged, for many cons associated with it. Though try your hands on this instead of calling repaint() call paintImmediately(int ...) or paintImmediately(Rectangle rect)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class PointsExample
{
private CustomPanel contentPane;
private Timer timer;
private int x = 1;
private int y = 1;
/*
* This is just JFrame, that we be
* using as the Base for our Application.
* Though here we are calling our
* JPanel (CustomPanel), whose
* paintComponent(...) method, we had
* override.
*/
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("Locate Mouse Position");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
contentPane = new CustomPanel();
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
for (int i = 0; i < 500; i++)
{
contentPane.set(x, y);
x++;
y++;
if (x == 450)
break;
}
}
public static void main(String\u005B\u005D args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new PointsExample().createAndDisplayGUI();
}
});
}
}
class CustomPanel extends JComponent
{
private int x;
private int y;
public void set(int a, int b)
{
x = a;
y = b;
paintImmediately(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize()
{
return (new Dimension(500, 500));
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.fillOval(x, y, 4, 4);
}
}
1: first line of paintComponent() should be your super.paintComponent()
2: why are you calling super.repaint(), make it simply repaint()
Your Drow should be like this.
public class drow extends JPanel {
...........
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 =(Graphics2D) g;
}
public void set_list(LinkedList <point> p){
Points =p;
repaint();
}
try with this.
i hope this is simply a structure, your paintComponent() isn't drawing anything.
EDIT
public void set_list(LinkedList <point> p){
Points =p;
System.out.println("set_ist");// 1:First this line will be displayed then..
repaint();//2: Then this is called, which in turn calls your `paintComponent()`
}
Now when your paintComponent() is called it has
system.out.println("paintComponent");
//3: so now this will be displayed.
Where is the problem here?
EDIT- SWING TIMER
Your code was ok, but the function processing is way faster than GUI updation, thats why you were unable to see the changes in front of you. The way you were doing, of calling thread.sleep() between function calls to slow down it's call, was not a good approach. For any timing thing's in swing, use swing timer, i changed your code for swing timer.
Using Swing Timer:
public class exampe extends JPanel implements ActionListener {
int x;
int y;
int temp = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(x - 2, y - 2, 4, 4);
}
public void set(int X, int Y) {
x = X;
y = Y;
}
public static void main(String args[]) {
JFrame frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
exampe ex = new exampe();
JScrollPane scroll = new JScrollPane(ex);
frame.getContentPane().add(scroll);
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer PointTimer = new Timer(1000, ex);
PointTimer.setInitialDelay(1000);
PointTimer.start();
System.out.println("started");
}
#Override
public void actionPerformed(ActionEvent e) {
// set(rand.nextInt(350), rand.nextInt(350));
set(temp+10,temp+10);
temp=temp+2;
repaint();
}
}

Categories