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);
}
}
}
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);
}
}
Vertical bars should be filling to the height of the applet. When the top is reached, a new bar should start filling next to the previous. Problem: When the new bar starts filling the previous paint() /bar is cleared
img how it is: http://bayimg.com/DAEoeaagm
img how it should be: http://bayimg.com/dAeOgAaGm
the code:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JApplet;
public class fillingbar extends JApplet implements Runnable{
int shifting=0,filling=0;
public void init()
{
Thread t= new Thread(this);
t.start();
setSize(400,250);
}
public void paint(Graphics g)
{
super.paint(g);
g.setColor(Color.GREEN);
g.fillRect(shifting,getHeight()-filling,20,filling);
g.setColor(Color.BLACK);
g.drawRect(shifting, getHeight()-filling, 20, filling);
}
public void run()
{
while(true)
{
repaint();
try{
if(shifting<getWidth())
{
if(filling<getHeight())
filling+=10;
else {
shifting+=20;
filling=0;
}
}
Thread.sleep(50);
}catch(Exception E){
System.out.println("Exception caught");
}
}
}
}
You only draw one rectangle in your paint method, and so it makes sense that only one will show.
If you need to draw more, do so, using a for loop that loops through perhaps a Rectangle ArrayList<Rectangle>.
Another way is to make shifting local and do a bit of simple math inside paintComponent to see what to draw and where. For instance, draw your completed bars inside of a for loop, for (int i = 0; i < filling / getHeight(); i++) {, and your yet to be completed bar up to filling % getHeight().
You should not draw directly within a JApplet but rather in the paintComponent method of a JPanel.
A Swing Timer is easier to use than a thread (for me at least), and can be safer.
For example, this can be created by the code below:
import java.awt.BorderLayout;
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.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
#SuppressWarnings("serial")
public class FillingBar2 extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
FillingBarPanel fillingBarPanel = new FillingBarPanel();
add(fillingBarPanel);
add(new JButton(new StartAction(fillingBarPanel)), BorderLayout.PAGE_END);
setSize(getPreferredSize());
}
});
} catch (InvocationTargetException | InterruptedException e) {
System.err.println("Big Problems");
e.printStackTrace();
}
}
}
#SuppressWarnings("serial")
class StartAction extends AbstractAction {
private FillingBarPanel fillingBarPanel;
public StartAction(FillingBarPanel fillingBarPanel) {
super("Start");
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
this.fillingBarPanel = fillingBarPanel;
}
#Override
public void actionPerformed(ActionEvent evt) {
fillingBarPanel.start();
}
}
#SuppressWarnings("serial")
class FillingBarPanel extends JPanel {
private static final int BAR_WIDTH = 20;
private static final int TIMER_DELAY = 100;
private static final int PREF_W = 400;
private static final int PREF_H = 250;
private int filling = 0;
private Timer timer;
public FillingBarPanel() {
timer = new Timer(TIMER_DELAY, new TimerListener());
}
public void start() {
if (timer != null && !timer.isRunning()) {
timer.start();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int shifting = 0;
for (int i = 0; i < filling / getHeight(); i++) {
shifting = i * BAR_WIDTH;
g.setColor(Color.GREEN);
g.fillRect(shifting, 0, BAR_WIDTH, getHeight());
g.setColor(Color.BLACK);
g.drawRect(shifting, 0, BAR_WIDTH, getHeight());
}
shifting = BAR_WIDTH * (filling / getHeight());
g.setColor(Color.GREEN);
g.fillRect(shifting, getHeight() - (filling % getHeight()), BAR_WIDTH, getHeight());
g.setColor(Color.BLACK);
g.drawRect(shifting, getHeight() - (filling % getHeight()), BAR_WIDTH, getHeight());
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent evt) {
filling += 10;
repaint();
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
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 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 want to display a GameOver image in a pacman game after lives are over. But I call the paintGameOverScreen(Graphics g) and then I need to initialize g. Is there any other way to do this?
This is my Lives class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
public class Lives{
private int lives;
public Lives() {
lives = 1;
}
public void removeLife() {
lives--;
if(lives==0){
System.out.println("END GAME");
paintGameOverScreen(g);
System.exit(0);
}
}
public void paintGameOverScreen(Graphics g) {
ImageIcon i = new ImageIcon("src\image");
Image image = i.getImage();
int x = 0;
int y = 0;
g.drawImage(image, x, y, 100,100,null);
}
public void paint(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(5*20, 25*20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6*20, 26*20);
}
}
You never call paint() or paintComponent() yourself, you always go through repaint() which will take care of setting up the appropriate Graphics
Just to show what #mKorbel is referring to:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Lives extends JPanel {
private int lives;
private ImageIcon gameOverImage;
public Lives() {
try {
gameOverImage = new ImageIcon(new URL("http://imgup.motion-twin.com/dinorpg/0/f/77acf80b_989624.jpg"));
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lives = 5;
}
public void removeLife() {
if (lives > 0) {
lives--;
System.out.println("Left lives: " + lives);
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (lives > 0) {
System.out.println("Still have " + lives + " lives");
g.setColor(Color.WHITE);
g.fillRect(5 * 20, 25 * 20, 100, 30);
g.setColor(Color.BLACK);
String result = "Lives: " + lives;
g.drawString(result, 6 * 20, 26 * 20);
} else if (gameOverImage != null) {
System.out.println("Game over");
int x = (getWidth() - gameOverImage.getIconWidth()) / 2;
int y = (getHeight() - gameOverImage.getIconHeight()) / 2;
g.drawImage(gameOverImage.getImage(), x, y, gameOverImage.getIconWidth(), gameOverImage.getIconHeight(), this);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame(Lives.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Lives lives = new Lives();
frame.add(lives);
frame.pack();
frame.setVisible(true);
// Dummy timer that reduces the lives every second. For demo purposes only of course
Timer t = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lives.removeLife();
}
});
t.start();
}
});
}
}
for public void paint(Graphics g) { is there missed container,
JPanel (in some cases JComponent) could be container for todays Java
have to use paintComponent instead of paint()
inside paintComponent you can to flag for paintGameOverScreen, then there paint only BufferedImage
prepare all Objects before, as local variable, do not load any FileIO (load images) inside paint(), paintComponent()
Here is how I did:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class InitializeGraphics
{
static BufferedImage buffer = null;
static int height = 10;
static int width = 10;
static Graphics2D g2;
public InitializeGraphics() {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2 = buffer.createGraphics();
g2.fillOval(2, 2, 2, 2);
g2.dispose();
}
protected void paintComponent(Graphics g) {
int x = 0;
int y = 0;
g.drawImage(buffer, x, y, width, height, null);
}
public Graphics2D getGraphics(){
return g2;
}
}
Then somewhere:
InitializeGraphics instance=new InitializeGraphics();
Graphics2D gG2 = instance.getGraphics();