I'm trying to make a game where numbers would fall from the top and if the player hits the number with the correct equation(math game), it will disappear.
The thing is, whenever I draw my background, the Jlabel keeps on displaying at the back of the giF background. Any ideas why??
This is like my core class.
package GAME;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class GameFrame extends JPanel implements ActionListener{
Image background = Toolkit.getDefaultToolkit().createImage("D:\\SHIPMATH TEXTURES\\simple-water-animation-597-x-800.gif");
Timer mainTimer;
shipPlayer player;
enemyShips enemy;
int level = 5;
int enemyCount = 5;
int numCount = 10;
// arraylists are arrays that has no capacity limit
static ArrayList<Numbers> number = new ArrayList<Numbers>();
static ArrayList<enemyShips> enemies = new ArrayList<enemyShips>();
static ArrayList<Cannons> cannon_balls = new ArrayList<Cannons>();
JLabel numbers;
Random rn = new Random();
public GameFrame() {
// calls the actionPerformed method every 10 milliseconds
mainTimer = new Timer(10, this);
mainTimer.start();
setLayout(null);
numbers = new JLabel("TEST");
numbers.setBounds(200, 200, 100, 100);
add(numbers);
setFocusable(true);
player = new shipPlayer(0, 500);
addKeyListener(new KeyAdapt(player));
startGame();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
// g2d.drawImage(background, 0, 0, this);
player.draw(g2d);
repaint();
for(int i=0; i<enemies.size(); i++) {
// the value of 'i' is the location/index used to find the value stored in the ArrayList
enemyShips enemy = enemies.get(i);
enemy.draw(g2d);
}
for(int i=0; i<cannon_balls.size(); i++) {
Cannons cannon = cannon_balls.get(i);
cannon.draw(g2d);
}
}
public void actionPerformed(ActionEvent e) {
player.update();
// where movement of the enemy happens
for(int i=0; i<enemies.size(); i++) {
enemyShips enemy = enemies.get(i);
enemy.update();
}
// Later...
for(int i=0; i<cannon_balls.size(); i++) {
Cannons cannon = cannon_balls.get(i);
cannon.update();
}
repaint();
}
public static void addEnemy(enemyShips e) {
enemies.add(e);
// stores what the user puts in the enemyShips' object into the ArrayList "enemies"
}
public static void removeEnemy(enemyShips e) {
enemies.remove(e);
// removes what the user inputs from the ArrayList
}
public static ArrayList<enemyShips> getEnemyList() {
return enemies;
}
public static void addNumbers(Numbers n) {
number.add(n);
}
public static void addCannons(Cannons c) {
cannon_balls.add(c);
// stores what the user puts in the Cannons' object into the ArrayList "cannon_balls"
}
public static void removeCannons(Cannons c) {
cannon_balls.remove(c);
// removes what the user inputs from the ArrayList
}
public static ArrayList<Cannons> getCannonsList() {
return cannon_balls;
}
public void startGame() {
// enemyCount = level * 5;
// runs 5 times
for(int x=0; x<enemyCount; x++) {
addEnemy(new enemyShips(rn.nextInt(500), -rn.nextInt(800)));
}
}
}
The fix for your issue is to rename your paint method to paintComponent. Here's what you are doing right now:
public void paint(Graphics g) {
super.paint(g); //this paints all of your JPanel's child components, including your JPanel
Graphics2D g2d = (Graphics2D) g;
// g2d.drawImage(background, 0, 0, this); // this is painting your background image over your JPanel.
player.draw(g2d);
...
If you use paintComponent instead, you won't have this problem with the child components being painted over
Related
Whenever I tried to run the program, yes the text is moving but it leaves a trace of its last position. Like for example the text "test" is in first positioned at x=50, y=100 then in the next update, the x=50, y=120. The problem is, the last position which is the x=50, y=100 is still in display. I would like to remove the last position of the text every time it updates.
Can you guys help me?
(The j label is in the Action performed method)
This is my core class.
package GAME;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
public class GameFrame extends JPanel implements ActionListener{
Image background = Toolkit.getDefaultToolkit().createImage("D:\\SHIPMATH TEXTURES\\simple-water-animation-597-x-800.gif");
Timer mainTimer;
shipPlayer player;
enemyShips enemy;
int level = 5;
int enemyCount = 5;
int numCount = 10;
int y=0;
// arraylists are arrays that has no capacity limit
static ArrayList<Numbers> number = new ArrayList<Numbers>();
static ArrayList<enemyShips> enemies = new ArrayList<enemyShips>();
static ArrayList<Cannons> cannon_balls = new ArrayList<Cannons>();
JLabel numbers;
Random rn = new Random();
public GameFrame() {
// calls the actionPerformed method every 10 milliseconds
mainTimer = new Timer(10, this);
mainTimer.start();
setLayout(null);
setFocusable(true);
player = new shipPlayer(0, 500);
addKeyListener(new KeyAdapt(player));
startGame();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(background, 0, 0, this);
player.draw(g2d);
repaint();
for(int i=0; i<enemies.size(); i++) {
// the value of 'i' is the location/index used to find the value stored in the ArrayList
enemyShips enemy = enemies.get(i);
enemy.draw(g2d);
}
for(int i=0; i<cannon_balls.size(); i++) {
Cannons cannon = cannon_balls.get(i);
cannon.draw(g2d);
}
}
public void actionPerformed(ActionEvent e) {
player.update();
// where movement of the enemy happens
for(int i=0; i<enemies.size(); i++) {
enemyShips enemy = enemies.get(i);
enemy.update();
}
// Later...
for(int i=0; i<cannon_balls.size(); i++) {
Cannons cannon = cannon_balls.get(i);
cannon.update();
}
/////////////////////////////////////
y++;
numbers = new JLabel("TEST");
numbers.setBounds(200, y, 100, 100);
add(numbers);
////////////////////////////////////
repaint();
}
public static void addEnemy(enemyShips e) {
enemies.add(e);
// stores what the user puts in the enemyShips' object into the ArrayList "enemies"
}
public static void removeEnemy(enemyShips e) {
enemies.remove(e);
// removes what the user inputs from the ArrayList
}
public static ArrayList<enemyShips> getEnemyList() {
return enemies;
}
public static void addNumbers(Numbers n) {
number.add(n);
}
public static void addCannons(Cannons c) {
cannon_balls.add(c);
// stores what the user puts in the Cannons' object into the ArrayList "cannon_balls"
}
public static void removeCannons(Cannons c) {
cannon_balls.remove(c);
// removes what the user inputs from the ArrayList
}
public static ArrayList<Cannons> getCannonsList() {
return cannon_balls;
}
public void startGame() {
// enemyCount = level * 5;
// runs 5 times
for(int x=0; x<enemyCount; x++) {
addEnemy(new enemyShips(rn.nextInt(500), -rn.nextInt(800)));
}
}
}
This is the output of this program.
Click here for the output of the program
you can use getContentPane().repaint() over and over in a loop or something else like some sort of timer.
How to make a list or an array of 10 coins appear on the game? It is the same coin image. I want my sprite (Mario) to pick up all 10 coins, but I want them next to each other } maybe I could manually type out the x locations.
Mainly I am not sure how to make the coins appear on the screen.
UPDATE: The coins appear when I manually type out each coin (exp. coins[0]=tool.kit..). It does not work with the for loop though.
public class Action extends JPanel implements ActionListener, KeyListener {
Timer t = new Timer(5, this);
private Image man;
int x=0, y=490, a=(int) (Math.random() * 450 + 1), b=500; // make a random num
Image img;
Image [] coins = new Image [10];
public Action() {
super.setDoubleBuffered(true);
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
img = Toolkit.getDefaultToolkit().createImage("background.png");
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
ImageIcon ii = new ImageIcon("realmario.png");
man = ii.getImage();
g.drawImage(img,0, 0, null);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(man, x, y, this);
g2d.drawImage(coins[1], a, b, this);
for (int i = 0; i<coins.length; i++) {
coins[i] = Toolkit.getDefaultToolkit().createImage("coin.png");
}
}
If you want to place objects one linked to other you have to work with x-axis and adjust properly.
Eg. img have (3,7) pixels and first will be at (10,10). You need to grab x = 3 and made adjustments in loop.
1: img at 10,10 (initial)
2: img at 10+3,10
3: img at 10+3+3,10 ; etc
//image dimension on x
int image_x = 3;
//initial placement on x,y
int x=10,y=10;
for(int i=0;i<coins.length; i++)
{
//if same image is enough one coin
g2d.drawImage(coin[7],x,y,this);
x=x+image_x;
}
Just check first the coordinates system.
Draw the same image 10 times, each with different coordinates:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SwingTest extends JFrame {
public SwingTest() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(new ImagePanel());
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new SwingTest());
}
}
class ImagePanel extends JComponent {
BufferedImage coin = getImage();
private static final int GAP =2;
public ImagePanel() {
setPreferredSize(new Dimension( 300, 200));
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
int x=0, y= GAP;
for (int i = 0; i <3 ; i++) {
g.drawImage(coin,x,y, this);
x= x+ GAP + coin.getWidth();
}
}
public static BufferedImage getImage() {
try {
URL url = new URL("http://www.btcwmx.ru/admin/uploads/img/bitcoin-gold.png");
return ImageIO.read(url);
} catch ( IOException ex) { ex.printStackTrace();}
return null;
}
}
Howcome this code below wont work? I want to add new Ovals to the ArrayList every 200 ms and display them and run them one by one. It works fine when Im running one particle s.runner(); but it doesnt seem to run all my particles.
MAIN:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.Timer;
public class ExempelGraphics extends JFrame implements ActionListener {
Timer t;
private int inc = 0;
ArrayList<Surface> particle = new ArrayList<>();
Surface s;
public ExempelGraphics() {
t = new Timer(10, this);
t.start();
s = new Surface(10, 10);
initUI();
}
private void initUI() {
add(s);
setSize(350, 250);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
// s.runner();
// add
if (inc++ % 20 == 0) {
particle.add(new Surface(10, 10));
}
// display
for (int i = 0; i < particle.size(); i++) {
Surface p = particle.get(i);
p.runner();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ExempelGraphics ex = new ExempelGraphics();
ex.setVisible(true);
}
});
}
}
GRAPHICS:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Surface extends JPanel {
private int locX = 0;
private int locY = 0;
public Surface(int locX, int locY) {
this.locX = locX;
this.locY = locY;
}
public void runner() {
locX = locX + 1;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillOval(locX, locY, 10, 10);
}
}
I think that you're program structure is broken. You should have only one JPanel here that does the drawing, that has its paintComponent overridden, and your Surface class should be a logical class and not a component class -- in other words, don't have it extend JPanel, and give it a public void draw(Graphics g) method where you draw the oval. Then have the drawing JPanel hold an ArrayList of these surfaces, and in the main JPanel's paintComponent method, iterate through the surfaces, calling each one's draw method.
Also your Timer's delay is not realistic and is too small. 15 would be much more realistic.
Also, don't call repaint() from within surface, since that will generate too many repaint calls unnecessarily. Instead call it from within the Timer's ActionListener after calling the runner methods on all the Surface objects.
Also note that every time you add a component to a JFrame's contentPane in a default fashion, you cover up the previously added components. If you go by my recommendations above, this isn't an issue since you'd only be adding that single JPanel to it.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class ExampleGraphics2 extends JPanel {
private static final int PREF_W = 650;
private static final int PREF_H = 500;
private static final int TIMER_DELAY = 20;
private List<Surface> surfaces = new ArrayList<>();
public ExampleGraphics2() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Surface surface : surfaces) {
surface.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
private int index = 0;
#Override
public void actionPerformed(ActionEvent e) {
index++;
index %= 20;
if (index == 0) {
surfaces.add(new Surface(10, 10));
}
for (Surface surface : surfaces) {
surface.runner();
}
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Example Graphics 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ExampleGraphics2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
package foo1;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class Surface {
private int locX = 0;
private int locY = 0;
public Surface(int locX, int locY) {
this.locX = locX;
this.locY = locY;
}
public void runner() {
locX = locX + 1;
}
public void draw(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillOval(locX, locY, 10, 10);
}
}
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);
}
}
}
I am trying to make a program that work like this:
In Window class every time I click on the button, the method panel2 of Panel is called: first it is drawing a first circle, then a second one (after the time defined in the timer). Then, I click again on the button, and it is drawing a fist circle, then a second one then a third one. etc.
The problem is that it when I click to obtain 3 circles appearing one after the other, the two first circles drawn at the previous step (before I pressed a second time the button) stay on the screen and only the third circle is drawn when i press the button (instead of having : first circle drawn, second circle drawn, third circle drawn). I hope I am clear.
Here is a simple code:
Window
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Window extends JFrame implements ActionListener{
int h = 2;
Panel b = new Panel();
JPanel container = new JPanel();
JButton btn = new JButton("Start");
JButton bouton = new JButton();
Panel boutonPane = new Panel();
public Window(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
btn.addActionListener(this);
top.add(btn);
container.add(top);
this.setContentPane(container);
this.setVisible(true);
}
public void window2(){
this.setTitle("ADHD");
this.setSize(1000,700);
this.setLocationRelativeTo(null);
if (h < 11){
boutonPane.panel2(h);
bouton.addActionListener(this);
boutonPane.add(bouton);
this.add(boutonPane);
this.setContentPane(boutonPane);
updateWindow2();
}
this.setVisible(true);
}
public void updateWindow2(){
boutonPane.panel2(h);
this.revalidate();
this.repaint();
}
public void actionPerformed(ActionEvent e){
if ((JButton) e.getSource() == btn){
System.out.println("pressed0");
window2();
}
if ((JButton) e.getSource() == bouton){
h++;
System.out.println("pressed" + h);
updateWindow2();
}
}
public static void main(String[] args){
Window w = new Window();
}
}
Panel
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Panel extends JPanel implements ActionListener{
int m;
int u=0;
int lgi, lrgi;
int [] ta;
Timer timer1 = new Timer(300, this);
Panel(){
}
public void panel2(int n){
m=n;
ta = new int [n];
for(int it=0; it<m;it++){
ta[it]=100*it;
}
timer1.start();
}
public void paintComponent(Graphics gr){
super.paintComponent(gr);
gr.setColor(Color.red);
for(int i=0;i<m;i++){
gr.fillOval(ta[i],ta[i], 150, 150);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
if(u<m){
u++;
revalidate();
repaint();
}
}
}
Your code needs use two int values to decide how many circles to draw and when:
The first int should be the count of current circles to draw, say called, currentCirclesToDraw.
The second int will be the number of circles to draw total.
If you use a List<Ellipse2D> like I suggest, then this number will be the size of the list. So if the List is called ellipseList, then the 2nd number will be ellipseList.size().
The first variable will be incremented in the timer up to the size of the list, but no larger, and will be used by paintComponent method to decide how many circles to draw.
Key point here: the first number, the currentCirclesToDraw, must be re-set to 0 when the button is pressed. This way your paintComponent method will start out drawing 0 circles, then 1, then 2, ...
For example, the paintComponent method could look like so:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
g2.fill(ellipseList.get(i));
}
}
I use the second term in the for loop conditional statement, i < currentCirclesToDraw && i < ellipseList.size() as an additional fail-safe to be sure that we don't try to draw more circles then we have in our list.
My Timer's ActionListener would increment the currentCirclesToDraw variable and call repaint. It would stop the Timer once currentCirclesToDraw reaches the size of the ellipseList:
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (currentCirclesToDraw < ellipseList.size()) {
currentCirclesToDraw++;
repaint();
} else {
// stop the Timer
((Timer)e.getSource()).stop();
}
}
}
And my button's actionPerformed method would reset currentCirclesToDraw to 0, would add a new Ellipse2D to my ellipseList (if we've not yet reached the MAX_CIRCLE_INDEX), would call repaint() to clear the JPanel, and would construct and start the Timer:
public void actionPerformed(java.awt.event.ActionEvent arg0) {
currentCirclesToDraw = 0; // this is key -- reset the index used to control how many circles to draw
if (ellipseList.size() < MAX_CIRCLE_INDEX) {
double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
double y = x;
double w = CIRCLE_WIDTH;
double h = CIRCLE_WIDTH;
ellipseList.add(new Ellipse2D.Double(x, y, w, h));
}
repaint(); // clear image
new Timer(TIMER_DELAY, new TimerListener()).start();
};
Edit 3/30/14
Note it all can be put together like this:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
/**
* http://stackoverflow.com/a/22714405/522444
* http://stackoverflow.com/questions/22712655/repaint-in-panel-method-not-updated
* #author Pete
*
*/
#SuppressWarnings("serial")
public class TimerCircles extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 700;
private static final Color CIRCLE_COLOR = Color.RED;
public static final int MAX_CIRCLE_INDEX = 11;
public static final int TIMER_DELAY = 300;
public static final int CIRCLE_WIDTH = 100;
private final List<Ellipse2D> ellipseList = new ArrayList<>();
private int currentCirclesToDraw = 0;
public TimerCircles() {
add(new JButton(new ButtonAction("New Circle", KeyEvent.VK_C)));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
g2.fill(ellipseList.get(i));
}
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
currentCirclesToDraw = 0; // this is key -- reset the index used to control how many circles to draw
if (ellipseList.size() < MAX_CIRCLE_INDEX) {
double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
double y = x;
double w = CIRCLE_WIDTH;
double h = CIRCLE_WIDTH;
ellipseList.add(new Ellipse2D.Double(x, y, w, h));
}
repaint(); // clear image
new Timer(TIMER_DELAY, new TimerListener()).start();
};
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (currentCirclesToDraw < ellipseList.size()) {
currentCirclesToDraw++;
repaint();
} else {
// stop the Timer
((Timer)e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
TimerCircles mainPanel = new TimerCircles();
JFrame frame = new JFrame("TimerCircles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}