paintComponent: paint different geometric shapes after delay each time - java

I want paintComponent method to paint different geometrical objects after certain delay each time.
I have seen answers with System.currentTimeMillis() function I want to use the Timer class. Can you help me?

You may start learning about threads in java. Here I just modified(by adding an Animator) to a sample of sun tutorials
The trick is that now thread(Animator below) updates a variable of the painter in every 0.5 seconds, and calls a repaint to the panel.
Here's the complete sample:
package com.swing.examples;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.swing.examples.BullsEyePanel.Animator;
/*
***************************************************************
* Silly Sample program which demonstrates the basic paint
* mechanism for Swing components.
***************************************************************
*/
public class SwingPaintDemo {
public static void main(String[] args) {
JFrame f = new JFrame("Aim For the Center");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
BullsEyePanel panel = new BullsEyePanel();
panel.add(new JLabel("BullsEye!", SwingConstants.CENTER), BorderLayout.CENTER);
f.getContentPane().add(panel, BorderLayout.CENTER);
f.pack();
f.show();
Animator animator = panel.new Animator(panel);
animator.start();
}
}
/**
* A Swing container that renders a bullseye background
* where the area around the bullseye is transparent.
*/
class BullsEyePanel extends JPanel {
private int i = 0;
public BullsEyePanel() {
super();
setOpaque(false); // we don't paint all our bits
setLayout(new BorderLayout());
setBorder(BorderFactory.createLineBorder(Color.black));
}
public Dimension getPreferredSize() {
// Figure out what the layout manager needs and
// then add 100 to the largest of the dimensions
// in order to enforce a 'round' bullseye
Dimension layoutSize = super.getPreferredSize();
int max = Math.max(layoutSize.width,layoutSize.height);
return new Dimension(max+100,max+100);
}
protected void paintComponent(Graphics g) {
Dimension size = getSize();
int x = 0;
int y = 0;
while(x < size.width && y < size.height) {
g.setColor(i%2==0? Color.red : Color.white);
g.fillOval(x,y,size.width-(2*x),size.height-(2*y));
x+=10; y+=10; i++;
}
}
class Animator extends Thread{
private BullsEyePanel painter;
private int sleepTime = 500; //half a second
public Animator(BullsEyePanel painter){
this.painter = painter;
}
public void run(){
while(true){
painter.i++;
painter.repaint();
try{
Thread.sleep(sleepTime);
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}

Related

Adding animations to JPanel

I've written some code that essentially animates a sequence of images and adds them to a frame when I run the file.
I want to implement a functionality where I can add this animation to two different areas of a JPanel that has a BorderLayout (North and West).
I want to do this using a button but I don't know how to do that. I am new to event handling and layout managers.
How do I go about this?
My code for the animation:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ImageSequence extends JPanel implements ActionListener {
private ImageSQPanel imageSQPanel;
private static int frameNumber = -1;
private Timer timer;
private void buildUI(Container container, Image[] arrows) {
int fps = 10;
int delay = 1000 / fps;
timer = new Timer(delay, this);
timer.setInitialDelay(0);
timer.setCoalesce(true);
imageSQPanel = new ImageSQPanel(arrows);
container.add(imageSQPanel, BorderLayout.CENTER);
}
private synchronized void startAnimation() {
if (!timer.isRunning()) {
timer.start();
}
}
public void actionPerformed(ActionEvent e) {
frameNumber++;
imageSQPanel.repaint();
}
class ImageSQPanel extends JPanel {
Image arrowAnimation[];
ImageSQPanel(Image[] arrowAnimation) {
this.arrowAnimation = arrowAnimation;
}
//Draw the current frame of animation.
public void paintComponent(Graphics g) {
super.paintComponent(g); //paint background
//Paint the frame into the image.
try {
g.drawImage(arrowAnimation[ImageSequence.frameNumber % 10], 0, 0, this);
} catch (ArrayIndexOutOfBoundsException e) {
//On rare occasions, this method can be called
//when frameNumber is still -1. Do nothing.
}
}
}
//Invoked only when this is run as an application.
public static void main(String[] args) {
Image[] waving = new Image[7];
for (int i = 1; i <= 7; i++) {
waving[i - 1] = Toolkit.getDefaultToolkit().getImage(
"/Users/sarthaksachdeva/Documents/IntelliJ Projects/Animation/src/images/Arrow" + i + ".png");
}
JFrame f = new JFrame("ImageSequenceTimer");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
ImageSequence controller = new ImageSequence();
controller.buildUI(f.getContentPane(), waving);
controller.startAnimation();
f.setSize(new Dimension(200, 200));
f.setVisible(true);
}
}

Moving a ball on the screen with buttons. Can't program the initial position.

So I'm doing this exercise, where I need to create a program that will be moving a little ball on the screen by pressing one of four buttons. I've completed it, but then I wanted to make the initial position to be in the center of the screen, so I assigned the values getWidth()/2 to xCoord and getHeight()/2 to yCoord (first I did it without the constructor, then when it didn't work I added the constructor and added repaint(), so the paintComponent() would be called) but the ball is still in the top left corner when I start the program. How can I fix this?
P.S. I will appreciate any comments on the code in general, as well. Thank you.
package movingaball;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingABall extends JFrame {
private JButton jbtLeft = new JButton("Left");
private JButton jbtRight = new JButton("Right");
private JButton jbtUp = new JButton("Up");
private JButton jbtDown = new JButton("Down");
private BallPanel ballPanel = new BallPanel();
public MovingABall () {
JPanel buttonPanel = new JPanel();
buttonPanel.add(jbtLeft);
buttonPanel.add(jbtRight);
buttonPanel.add(jbtUp);
buttonPanel.add(jbtDown);
this.add(ballPanel);
this.add(buttonPanel, BorderLayout.SOUTH);
jbtLeft.addActionListener(new ButtonListener());
jbtRight.addActionListener(new ButtonListener());
jbtUp.addActionListener(new ButtonListener());
jbtDown.addActionListener(new ButtonListener());
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
MovingABall mainWondow = new MovingABall();
mainWondow.setTitle("Moving a ball");
mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWondow.setSize(300, 200);
mainWondow.setVisible(true);
}
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent buttonPressed) {
if (buttonPressed.getSource() == jbtLeft)
ballPanel.left();
else if (buttonPressed.getSource() == jbtRight)
ballPanel.right();
else if (buttonPressed.getSource() == jbtUp)
ballPanel.up();
else if (buttonPressed.getSource() == jbtDown)
ballPanel.down();
}
}
class BallPanel extends JPanel {
private int xCoord = 10;
private int yCoord = 10;
public BallPanel() {
xCoord = getWidth()/2;
yCoord = getHeight()/2;
repaint();
}
#Override
public void setBackground(Color bg) {
super.setBackground(bg); //To change body of generated methods, choose Tools | Templates.
}
public void left() {
xCoord-=5;
repaint();
}
public void right() {
xCoord+=5;
repaint();
}
public void up() {
yCoord-=5;
repaint();
}
public void down() {
yCoord+=5;
repaint();
}
protected void paintComponent(Graphics aBall) {
super.paintComponent(aBall);
System.out.println("X" + getWidth());
aBall.drawOval(xCoord, yCoord, 10, 10);
}
}
}
You Could
Use a ComponentListener and listener for ComponentResized events, but this could be called multiple times before the component reaches it's final screen size....
You Could
Use a AncestorListener listening for ancestorAdded, but it suffers from the same problem as the ComponentListener
You Could
Use a HierarchyListener listening for hierarchyChanged, but it suffers from the same problem as the ComponentListener and AncestorListener
You Could
Override doLayout, but this suffers from the same problem as the ComponentListener and AncestorListener, HierarchyListener...
So what to do?
What we need is to know when the component is resized for the last time when it is first shown. From my testing I found doLayout and hierarchyChanged of HierarchyListener to be good candidates.
Now the problem arises because we only want to use them until we get onto the screen, then after that, we don't care...
So, the first thing we need to is initialise the x/yCoords to some "invalid" value...
private int xCoord = -1;
private int yCoord = -1;
This gives us some clue that we still need to "set" the coordinates...
Next, we need some way we can set up an interruptible call back. Some way to way inject a short delay between our chosen "listener" and the time we actually update the coordinates, but which can be reset if the "listener" is triggered....
javax.swing.Timer is an excellent choice for this. I can wait in the background for a specified period of time and can be restarted should we need it...
private Timer resizeTimer;
public BallPanel() {
resizeTimer = new Timer(125, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Only update the coorinates if they are invalid...
if (xCoord < 0 && yCoord < 0) {
xCoord = getWidth() / 2;
yCoord = getHeight() / 2;
repaint();
}
}
});
resizeTimer.setRepeats(false);
Finally, need to "restart" the timer when our chosen "listener" is triggered.
For simplicity, I went for doLayout....
#Override
public void doLayout() {
super.doLayout();
if (xCoord < 0 && yCoord < 0) {
resizeTimer.restart();
}
}
Now, you might need to play around with the delay, I found 250 milliseconds to slow, but that's just me ;)
See comments in code.
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class MovingABall extends JFrame {
private JButton jbtLeft = new JButton("Left");
private JButton jbtRight = new JButton("Right");
private JButton jbtUp = new JButton("Up");
private JButton jbtDown = new JButton("Down");
private BallPanel ballPanel = new BallPanel();
public MovingABall () {
JPanel buttonPanel = new JPanel();
buttonPanel.add(jbtLeft);
buttonPanel.add(jbtRight);
buttonPanel.add(jbtUp);
buttonPanel.add(jbtDown);
ballPanel.setBackground(Color.RED);
this.add(ballPanel);
this.add(buttonPanel, BorderLayout.SOUTH);
jbtLeft.addActionListener(new ButtonListener());
jbtRight.addActionListener(new ButtonListener());
jbtUp.addActionListener(new ButtonListener());
jbtDown.addActionListener(new ButtonListener());
}
public static void main(String[] args) {
// Should be called on the EDT!
MovingABall mainWondow = new MovingABall();
mainWondow.setTitle("Moving a ball");
mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Don't pack here. Instead return a preferred size for the
// custom comonent end..
//mainWondow.setSize(300, 200);
// ..pack() the window.
mainWondow.pack();
mainWondow.setVisible(true);
}
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent buttonPressed) {
if (buttonPressed.getSource() == jbtLeft)
ballPanel.left();
else if (buttonPressed.getSource() == jbtRight)
ballPanel.right();
else if (buttonPressed.getSource() == jbtUp)
ballPanel.up();
else if (buttonPressed.getSource() == jbtDown)
ballPanel.down();
}
}
class BallPanel extends JPanel {
private int xCoord = -1;
private int yCoord = -1;
private Dimension preferredSize = new Dimension(300,200);
/* Harmful to our logic..
public BallPanel() {
xCoord = getWidth()/2;
yCoord = getHeight()/2;
repaint();
}
*/
/* A good compiler would remove this..
#Override
public void setBackground(Color bg) {
super.setBackground(bg);
} */
public void left() {
xCoord-=5;
repaint();
}
public void right() {
xCoord+=5;
repaint();
}
public void up() {
yCoord-=5;
repaint();
}
public void down() {
yCoord+=5;
repaint();
}
/** Suggest a size to the layout manager. */
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
protected void paintComponent(Graphics aBall) {
super.paintComponent(aBall);
// This will center the ball if it is the first time painted
// OR if the x or y co-ord goes off the left/top edge.
// Further logic left to user..
if (xCoord<0 || yCoord<0) {
xCoord = getWidth()/2;
yCoord = getHeight()/2;
}
System.out.println("X" + getWidth());
aBall.drawOval(xCoord, yCoord, 10, 10);
}
}
}
You're on the right track, but calling the getWidth() and getHeight() methods too soon. They'll still return zero cause the size has not yet been set:
GetWidth() and getHeight() get called here, in the constructor:
MovingABall mainWondow = new MovingABall();
mainWondow.setTitle("Moving a ball");
mainWondow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
But size gets set here:
mainWondow.setSize(300, 200);
mainWondow.setVisible(true);
So when to act then? You could override setSize() or setVisible() in your MovingABall class or listen for componentShown()
Listen for when a Component is Shown for the First Time suggests a HierarchyListener, which may be the most reliable approach, but a bit overkill here.
EDIT:
The simplest thing that could possibly work is simpler still, I just realized: Initialize the BallPanel not to 10,10 but to 150,100 :)
Somewhat more seriously:
Add the dimension to both MainWindow and BallPanel's constructors instead of calling setSize on the MainWindow and you'll reliably get a consistent initial state without any hassle.

Strange JFrame Behavior

I have the following program which has some very strange and unwanted behavior when it runs. Its supposed to have two buttons, "Start" and "Stop, but when I click "Start" another button shows up right below "Start". Here's a print screen of what I'm talking about:
What am I doing wrong and how do I fix this ugly problem?
Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if(!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int)(Math.random()*256);
int blue = (int)(Math.random()*256);
int green = (int)(Math.random()*256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth()-wd);
int y = rand.nextInt(getHeight()-ht);
g.fillRect(x,y,wd,ht);
}
} // close inner class
}
Also I'm trying to get the Start button to do two things. One is to of course start the animation but when the Stop button is pressed and I press Start again, I want it to clean the screen so to speak and start the animation again a new. Any tips on that?
You do not call super.paintComponent(Graphics g) in overriden paintComponent(..) method which you should in order to honor the paint chain and thus the painting of other components.
This call should also be the first call within the method:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//do painting here
}
A probem might arise that drawings are not persistent. You must than have a way to store drawings and redraw every time. The most common is an ArrayList which will hold objects to be drawn (thus you cann add to the list remove etc), you would than iterate over the list and redraw each object in paintComponent. See my answer here for an example.
Also please remember to create and manipulate Swing components on Event Dispatch Thread :
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//create UI and components here
}
});
Dont call setSize(..) on JFrame rather override getPreferredSize() of JPanel and return an appropriate height which fits all components, than call JFrame#pack() before setting JFrame visible (but after adding all components).
No need for getContentPane().add(..) as of Java 6+ add(..) defaults to contentPane
Do not re declare Random i.e Random r=new Random() each time paintComponent is called as this will make the distributions of the values less random rather initiate it once when class is created and call methods on the instance
Here is the fixed code (with above fixes implemented):
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class TwoButtonsTest {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TwoButtonsTest test = new TwoButtonsTest();
test.go();
}
});
}
final DrawPanel myDraw = new DrawPanel();
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
frame.add(myDraw, BorderLayout.CENTER);
frame.add(startButton, BorderLayout.NORTH);
frame.add(stopButton, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
//needs to be implemented
if (!isClicked) {
}
myDraw.clearRects();
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
private ArrayList<MyRectangle> rects = new ArrayList<>();
private Random rand = new Random();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
addRect();
for (MyRectangle r : rects) {
g.setColor(r.getColor());
g.fillRect(r.x, r.y, r.width, r.height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
public void clearRects() {
rects.clear();
}
public void addRect() {
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
rects.add(new MyRectangle(x, y, wd, ht, new Color(red, blue, green)));
}
} // close inner class
}
class MyRectangle extends Rectangle {
Color color;
public MyRectangle(int x, int y, int w, int h, Color c) {
super(x, y, w, h);
this.color = c;
}
public Color getColor() {
return color;
}
}
I wish I could offer a solution, but as of yet I haven't found one. I can tell you the root of the "problem" here lies in the way you are drawing the Center section of your BorderLayout. You are overriding the whole paintComponent() function for this program and having whatever it creates put into the Center of your BoarderLayout. In this case, each time you click a button, the program calls the repaint to draw the image of a clicked button, but since you have also added ANY of the drawn objects to the Center panel, it also is drawn there. Since this specific repaint doesn't specify a location, it goes in the upper left corner.
I fixed your button problem on my Windows XP computer by invoking SwingUtilities.
I formatted your Java code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TwoButtonsTest implements Runnable {
JFrame frame;
Timer timer;
boolean isClicked;
public static void main(String[] args) {
SwingUtilities.invokeLater(new TwoButtonsTest());
}
#Override
public void run() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JButton startButton = new JButton("Start");
startButton.addActionListener(new StartListener());
JButton stopButton = new JButton("Stop");
stopButton.addActionListener(new StopListener());
final DrawPanel myDraw = new DrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, myDraw);
frame.getContentPane().add(BorderLayout.NORTH, startButton);
frame.getContentPane().add(BorderLayout.SOUTH, stopButton);
frame.setVisible(true);
timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
myDraw.repaint();
}
});
}
class StartListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
if (!isClicked) {
}
isClicked = true;
timer.start();
}
}
class StopListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// needs to be implemented
timer.stop();
isClicked = false;
}
}
class DrawPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
} // close inner class
}
To clean the screen when you press the Start button, you're going to have to add some methods to your DrawPanel class.
Here's one way to do it.
class DrawPanel extends JPanel {
protected boolean eraseCanvas;
public void setEraseCanvas(boolean eraseCanvas) {
this.eraseCanvas = eraseCanvas;
}
#Override
public void paintComponent(Graphics g) {
if (eraseCanvas) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
} else {
int red = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
g.setColor(new Color(red, blue, green));
Random rand = new Random();
// following 4 lines make sure the rect stays within the frame
int ht = rand.nextInt(getHeight());
int wd = rand.nextInt(getWidth());
int x = rand.nextInt(getWidth() - wd);
int y = rand.nextInt(getHeight() - ht);
g.fillRect(x, y, wd, ht);
}
}
} // close inner class

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();
}
}

Basic animation not performing

I am learning java. This is my first animation. I want a ball to move up and down continuously when start button is pressed, and it should STOP when stop button is pressed. The code I have written moves the ball 5 times(3 times down and 2 times up). But the panel displays only start and final positions, it does not display intermediate positions. How to display intermediate positions as well?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class dabble
{
private boolean z = true;
private int x=10;
private int y=10;
private JFrame frame;
private JLabel label;
private mypanel panel;
private JButton b1;
private JButton b2;
public static void main (String[] args)
{
dabble dab = new dabble();
dab.start();
}
void start()
{
frame = new JFrame();
label = new JLabel();
panel = new mypanel();
b1= new JButton("Start");
b2= new JButton("Stop");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b1.addActionListener(new al1());
b2.addActionListener(new al2());
frame.getContentPane().add(BorderLayout.NORTH,b1);
frame.getContentPane().add(BorderLayout.SOUTH,b2);
frame.getContentPane().add(BorderLayout.CENTER,panel);
frame.getContentPane().add(BorderLayout.EAST,label);
frame.setSize(600,600);
frame.setVisible(true);
}
void go()
{
for(int i=0;i<5;i++)
{
if(z==false)
break;
//label.setText("Hi");
y=510-y;
panel.repaint();
try{
Thread.sleep(500);
//label.setText("sleep");
}catch(Exception Ex)
{
//label.setText("exp");
}
}
}
class al1 implements ActionListener{
public void actionPerformed(ActionEvent event){
go();
}
}
class al2 implements ActionListener{
public void actionPerformed(ActionEvent event){
z=false;
}
}
class mypanel extends JPanel
{
public void paintComponent ( Graphics g)
{
g.setColor(Color.white);
g.fillRect(0,0,this.getWidth(),this.getHeight());
int red = (int) (Math.random()*255);
int green = (int) (Math.random()*255);
int blue = (int) (Math.random()*255);
Color c1 = new Color(red,green,blue);
g.setColor(c1);
g.fillOval(x,y,20,20);
}
}
}
Calling repaint() does not actually paint the panel - it just marks it to be painted later. And painting always happens on the event dispatch thread, as do event listener notifications.
Since go() is being called on the event dispatch thread (by a button action listener), the panel cannot be repainted while go() is running. You simply queue up a single repaint that happens as soon as go() is done.
What you probably want to do is to use a javax.swing.Timer that fires once every 500 ms, and have its action be to move the ball one step and then call repaint().
Well you perform your loop 5 times (instead of an infinite loop). Moreover you change the vertical position from 10 to 500 and then back to 10 etc... If you want to see intermediate position, you should consider asking to repaint the intermediate positions as well providing the intermediate values of y.
OK, doing the following solves your issues but it is definitely a poor design:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class dabble {
private static final long ANIMATION_DURATION = 5000; // 5 seconds
private static final int REFRESH_RATE = 10;// 10 times per second
private boolean incrementing = true;
private volatile boolean z = true;
private int x = 10;
private volatile int y = 10;
private JFrame frame;
private JLabel label;
private mypanel panel;
private JButton b1;
private JButton b2;
public static void main(String[] args) {
dabble dab = new dabble();
dab.start();
}
void start() {
frame = new JFrame();
label = new JLabel();
panel = new mypanel();
b1 = new JButton("Start");
b2 = new JButton("Stop");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b1.addActionListener(new al1());
b2.addActionListener(new al2());
frame.getContentPane().add(BorderLayout.NORTH, b1);
frame.getContentPane().add(BorderLayout.SOUTH, b2);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.getContentPane().add(BorderLayout.EAST, label);
frame.setSize(600, 600);
frame.setVisible(true);
}
void go() {
new Animation().start();
}
class al1 implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
go();
}
}
class al2 implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
z = false;
}
}
class mypanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color c1 = new Color(red, green, blue);
g.setColor(c1);
g.fillOval(x, y, 20, 20);
}
}
class Animation extends Thread {
private long start;
#Override
public void run() {
start = System.currentTimeMillis();
while (true) {
if (!z) {
return;
}
double progress = (double) (System.currentTimeMillis() - start) / ANIMATION_DURATION;
System.err.println(progress);
if (incrementing) {
y = (int) (10 + 500 * progress);
} else {
y = (int) (510 - 500 * progress);
}
if (progress > 1.0) {
start = System.currentTimeMillis();
incrementing = !incrementing;
}
panel.repaint();
try {
Thread.sleep(1000 / REFRESH_RATE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}

Categories