I am making a flappy bird like plane game, and in one class I have the background that moves, and then I add it to the jPanel in the main which is in its own class, in another class I have the player and I add that to the main Jpanel. But whichever class I add first dissapears when I add the second class to the JPanel. Here is my background class:
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class map1 extends JPanel implements ActionListener{
Timer t = new Timer(5, this);
int rect1x = 1000, rect1y = 0;
int rect2x = 1250, rect2y = 0;
int rect3x = 1500, rect3y = 0;
int rect4x = 1750, rect4y = 0;
int rect5x = 2000, rect5y = 0;
int rect1dx = 1000, rect1dy = 300;
int rect2dx = 1250, rect2dy = 350;
int rect3dx = 1500, rect3dy = 400;
int rect4dx = 1750, rect4dy = 350;
int rect5dx = 2000, rect5dy = 300;
public map1(){t.start();}
public void rectset1(Graphics g){
g.drawRect(rect1x, rect1y, 50, 100);
g.drawRect(rect1dx, rect1dy, 50, 300);
}
public void rect2(Graphics g){
g.drawRect(rect2x, rect2y, 50, 150);
g.drawRect(rect2dx, rect2dy, 50, 300);
}
public void rect3(Graphics g){
g.drawRect(rect3x, rect3y, 50, 200);
g.drawRect(rect3dx, rect3dy, 50, 300);
}
public void rect4(Graphics g){
g.drawRect(rect4x, rect4y, 50, 150);
g.drawRect(rect4dx, rect4dy, 50, 300);
}
public void rect5(Graphics g){
g.drawRect(rect5x, rect5y, 50, 100);
g.drawRect(rect5dx, rect5dy, 50, 300);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
rectset1(g);
rect2(g);
rect3(g);
rect4(g);
rect5(g);
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
rect1x--;
rect2x --;
rect3x --;
rect4x --;
rect5x --;
rect1dx --;
rect2dx --;
rect3dx --;
rect4dx --;
rect5dx --;
}
}
And here is my plane class:
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class player extends JPanel implements ActionListener, KeyListener {
int x = 200, y = 300;
Timer t = new Timer(5, this);
public void space(){
y+=20;
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawOval(x, y, 50, 50);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if(code == KeyEvent.VK_SPACE){space();}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
y--;
}
}
And lastly my main:
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class testMain extends JFrame{
public testMain(){}
public static void main(String args[]){
map1 m = new map1();
player q = new player();
JFrame j = new JFrame();
j.setSize(800,600);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.add(m);
j.add(q);
j.setVisible(true);
}
}
You should have a single JPanel that paints itself by overriding paintComponent(). That JPanel class should have a reference to anything that needs to be drawn. From the paintComponent() method, you just iterate over each game object that needs to be drawn and draw it. Better yet, each game object should have a drawMe(Graphics g) method that draws itself, and your paintComponent() method can just pass its Graphics instance into each of those methods.
public class GamePanel extends JPanel{
//example GameObjects, these could be in a list
GameObject bird = new Bird();
GameObject cloud = new Cloud();
public void paintComponent(Graphics g){
bird.drawMe(g);
cloud.drawMe(g);
}
}
First off in your main class you are extending JFrame but then instantiating it as well which is redundant. You should do either of the two, not both!
Ex:
public class main extends JFrame{
public main(){
setSize(800,600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(m);
add(q);
setVisible(true);
}
}
Secondly answering your question, you should always have a single class that does all the drawing for your game. Lets call this the Board class. You could have other classes that have functions to draw, but these functions should be called in your Board class within the paintComponent() function.
Note: Also make sure to do this -
void paintComponent(Graphics g){
super.paintComponent(g);
......
g.dispose();
}
Related
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 was programming a game similar to asteroid, but I do not understand how to spawn the asteroids in the background.
now i spawn an asteroid in the main class but i want create a class for the asteroid ho i do it?
MAIN CLASS
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if(flag_img)
{
background(g2d);
logo(g2d);
menu(g2d);
spaceship(g2d);
crediti(g2d);
}
}
background function(now)
private void background(Graphics2D g2d)
{
asteroidi_g_x+=r.nextInt(4);
asteroidi_g_y+=r.nextInt(1);
g2d.drawImage(asteroidi_g[0], asteroidi_g_x,asteroidi_g_y,this);
}
background function(what i want)
private void background(Graphics2D g2d)
{
asteroid asteroid = new asteroid[10];
}
and class asteroid
public class asteroid extends JPanel implements ActionListener
{
private BufferedImage images_asteroid;
private boolean flag_img;
private JPanel jp;
private int x,y;
public asteroide_grande(JPanel jp)
{
flag_img = true;
x = (jp.getWidth()/2);
y = (jp.getHeight()/2);
this.jp = jp;
try {
images_asterod = ImageIO.read(this.getClass().getResource("images/asteroid/a1.png"));
} catch(IOException e){flag = false;}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(flag_img)
{
g.drawImage(images_asteroid, 100, 100,this);
}
}
#Override
public void actionPerformed(ActionEvent e)
{
x=x-1;
y=y+1;
repaint();
}
method paintcomponent in class doesn't work
Don't have your Asteroid class extends JPanel. Instead have it as a class that model's asteroid data and has data manipulation methods. You'll also want to have a draw method that take a Graphic context. Something like
public class Asteroid {
Image asteroidImage;
JPanel panel;
int x, y;
public Asteroid(JPanel panel, Image image, int x, int y) {
this.panel = panel;
this.asteroidImage = image;
this.x = x;
this.y = y;
}
public void drawAsteroid(Graphics g) {
g.drawImage(asteroidImage, x, y, panel);
}
public void move() {
x += 5;
}
}
Now you have a model of an asteroid, you can create a List of Asteriod objects and iterate through them and use it's drawAsteroid method to paint them. Something like
public class GamePanel extends JPanel {
List<Asteroid> asteroids;
Image asteroidImage;
public GamePanel(){
asteroidImage = ...
asteroids = new ArrayList<>();
asteroids.add(new Asteroid(GamePanel.this, asteroidImage, 100, 100));
// add more asteriods
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Asteriod asteroid: asteriods) {
asteriod.drawAsteroid(g);
}
}
}
To animate them, you'll want to use a javax.swing.Timer. See more at How to Use Swing Timers. You'll want to manipulate the Asteriod data in the Timer. With the code provided above, you can just call it's move method, then call repaint(). Something like
public GamePanel(){
...
Timer timer = new Timer(30, new ActionListener(){
public void actionPerformed(ActionEvent e) {
Iterator it = asteroids.iterator();
while (it.hasNaext()) {
Asteroid asteriod = (Asteroid)it.next();
asteroid.move();
}
}
});
}
You can see a bunch more complete example of animating multiple objects here and here and here and here and here
Here's a full example. You'll see I included a Rectangle2D object in the Astreroid class. That's just if you want to check for collision detection. You should move the Rectangle2D x and/or y with every Asreroid movement of x and y. Then you can check if asteroid.rectangle.intersects(someOtherObject)
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class AsteroidBackground extends JPanel {
private static final int D_W = 400;
private static final int D_H = 600;
BufferedImage asteroidImage;
BufferedImage background;
List<Asteroid> asteroids;
Random random = new Random();
int countToAddAsteroid = 0;
int y;
public AsteroidBackground() {
try {
asteroidImage = ImageIO.read(getClass().getResource("/resources/small-asteroid.png"));
background = ImageIO.read(getClass().getResource("/resources/space.png"));
} catch (IOException ex) {
Logger.getLogger(AsteroidBackground.class.getName()).log(Level.SEVERE, null, ex);
}
asteroids = new ArrayList<>();
y = 0 - asteroidImage.getHeight();
Timer timer = new Timer(40, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (countToAddAsteroid >= 25) {
int randX = random.nextInt(D_W);
asteroids.add(new Asteroid(AsteroidBackground.this, asteroidImage, randX, y));
countToAddAsteroid = 0;
}
countToAddAsteroid++;
Iterator it = asteroids.iterator();
while (it.hasNext()) {
Asteroid asteroid = (Asteroid)it.next();
if (asteroid.y >= D_H) {
it.remove();
} else {
asteroid.move();
}
}
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, this);
for (Asteroid asteroid : asteroids) {
asteroid.drawAsteroid(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Asteroid {
Rectangle2D rectangle;
Image asteroidImage;
JPanel panel;
int x, y;
public Asteroid(JPanel panel, Image image, int x, int y) {
this.panel = panel;
this.asteroidImage = image;
this.x = x;
this.y = y;
rectangle = new Rectangle2D.Double(
x, y, image.getWidth(panel), image.getHeight(panel));
}
public void drawAsteroid(Graphics g) {
g.drawImage(asteroidImage, x, y, panel);
}
public void move() {
y += 5;
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new AsteroidBackground());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
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();
}
});
}
}
I've been trying to do this assignment and for it to work I need the event mousePressed to work but for some reason it's not responding to the mouse. Its purpose is to paint another yellow circle when the mouse is pressed.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CatchMonster extends JPanel
{
private int height = 300;
private int width = 600;
private final int delay = 4001;
private ImageIcon image;
private Timer timer;
private int x, y, moveX, moveY, xPoint, yPoint;
public CatchMonster() {
DotListener dot = new DotListener();
addMouseListener(dot);
timer = new Timer(delay, new timerListener());
x = 40;
y = 40;
moveX = moveY = 3;
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
g.setColor(Color.yellow);
g.fillOval(x, y, 60, 60);
}
private class timerListener implements ActionListener
{
public void actionPerformed(ActionEvent e) {
Random gn = new Random();
x = gn.nextInt(width);
y = gn.nextInt(height);
repaint();
}
}
private class DotListener implements MouseListener
{
public void mousePressed(MouseEvent event)
{
repaint();
}
#Override
public void mouseClicked(MouseEvent event) {
}
#Override
public void mouseEntered(MouseEvent event) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent event) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent event) {
// TODO Auto-generated method stub
}
}
}
I need the event mousePressed to work but for some reason it's not responding to the mouse. Its purpose is to paint another yellow circle when the mouse is pressed.
But it won't paint another circle as all it does is call repaint(), and how is that going to paint anything new? If you want it to create another circle, you'll have to give it logic to do so. For instance, if you want to paint more than one yellow oval, you'll want to create an ArrayList of Point objects and add a Point into that array list in the mousePressed method. Then in the paintComponent method, you can iterate through the array list, painting ovals for each Point it contains.
In addition, you want to change this:
public void paintComponent(Graphics g) {
super.paintComponents(g); // this is not the "super" method of paintComponent
g.setColor(Color.yellow);
g.fillOval(x, y, 60, 60);
}
to this:
public void paintComponent(Graphics g) {
super.paintComponent(g); // See the difference?
g.setColor(Color.yellow);
g.fillOval(x, y, 60, 60);
}
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CatchMonster extends JPanel {
private int height = 300;
private int width = 600;
private final int delay = 4001;
private ImageIcon image;
private Timer timer;
private int x, y, moveX, moveY, xPoint, yPoint;
private List<Point> points = new ArrayList<Point>();
public CatchMonster() {
DotListener dot = new DotListener();
addMouseListener(dot);
timer = new Timer(delay, new timerListener());
x = 40;
y = 40;
moveX = moveY = 3;
setPreferredSize(new Dimension(width, height));
setBackground(Color.black);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.yellow);
g.fillOval(x, y, 60, 60);
int radius = 30;
g.setColor(Color.green);
for (Point p : points) {
int x = p.x - radius;
int y = p.y - radius;
g.fillOval(x, y, 2 * radius, 2 * radius);
}
}
private class timerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Random gn = new Random();
x = gn.nextInt(width);
y = gn.nextInt(height);
repaint();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new CatchMonster());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private class DotListener extends MouseAdapter {
public void mousePressed(MouseEvent event) {
points.add(event.getPoint());
repaint();
}
}
}