acroscene acroscene - 1 month ago 16
Java Question

List of graphics2D

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

Answer

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