Name55555555 Name55555555 - 7 months ago 11
Java Question

Added items to JPanel dont' show up

I was hoping anyone could help me with this. I can't really find anyone describing this exact issue. In my main class (

Window
) that extends
JFrame
I add an object of a class (
Panel
) that extends
JPanel
and add it to my frame.

So far so good, everything in the
JPanel
class shows up as they should, but when I in my main class create
ShapeDef
object (used to define and draw figures), and then try to add it to the
Panel
, the figures won't show up. Why? I tried using
revalidate()
and
repaint
, but it doesn't seem to work.

Here's some of the code:

Main-class:

public class Window extends JFrame implements ActionListener{

private JPanel myPanel;
private ShapeDef rect1, rect2;

public Window(){
super("Test Window");
setLayout(new BorderLayout());

myPanel = new Panel(new BorderLayout());
myPanel.setBackground(Color.BLACK);

rect1 = new ShapeDef("Rectangle", Color.green, 200, 300, 20, 80);
rect2 = new ShapeDef("Rectangle", Color.BLUE, 300, 700, 50, 40);
myPanel.add(rect1);
myPanel.add(rect2);

add(myPanel, BorderLayout.CENTER);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {

@Override
public void run() {
JFrame myFrame = new PongInvaders();
myFrame.setSize(1280, 720);
myFrame.setBackground(Color.BLACK);
myFrame.pack();
myFrame.setVisible(true);
}
});
}


ShapeDef class:

public class ShapeDef extends JComponent{
private final Color color;
private final int x, y, width, height;
private final String type;

public ShapeDef(String type, Color color, int x, int y, int width, int height){
this.type = type;
this.color = color;
setBounds(x, y, width, height);
this.x = x;
this.y = y;
this.width = width;
this.height = height;

}

@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
if(type.equalsIgnoreCase("Rectangle")){
g.setColor(color);
g.fillRect(0, 0, width, height);
}
else if(type.equalsIgnoreCase("Oval")){
g.setColor(color);
g.fillOval(x, y, width, height);
}
}
}


The current version of the code suggested by Keqiang Li draws both of the rectangles, but rect2 spawns at (0,0) while rect1 spawns at the correct coordinates. So basically the question is answered, so thank you all for your help. However why is it that only one of the rectangles spawn at (0,0) while the other spawns at set coordinates, when they're both added to the same JPanel?

@MadProgrammer Although your answer was not the one I expected, I think it's the one I needed. You definitely live up to your user name. Thank you so much for taking the time to teach me a little more about creating a better code structure.

I would also like to point out that the other answers were very helpful and enlightening, and that they also taught me alot.

Answer

Personally, I think you're approaching the solution from the wrong angle. Components are really designed to do this, sure you can make them do it, but it leads down some interesting paths which could have you bleeding in the gutter if you're not careful.

Instead, I'd define a "basic" shape which can be "painted" and from, define all you other shapes.

public interface Drawable {
    public Color getStrokeColor();
    public Color getFillColor();
    public Rectangle getBounds();
    public void paint(Graphics2D g2d);
}

public abstract class AbstractDrawable implements Drawable {

    private Color strokeColor;
    private Color fillColor;

    public AbstractDrawable(Color strokeColor, Color fillColor) {
        this.strokeColor = strokeColor;
        this.fillColor = fillColor;
    }

    @Override
    public Color getStrokeColor() {
        return strokeColor;
    }

    @Override
    public Color getFillColor() {
        return fillColor;
    }

}

public class RectangleDrawable extends AbstractDrawable {

    private Rectangle bounds;

    public RectangleDrawable(int x, int y, int width, int height, Color strokeColor, Color fillColor) {
        super(strokeColor, fillColor);
        bounds = new Rectangle(x, y, width, height);
    }

    @Override
    public Rectangle getBounds() {
        return bounds;
    }

    @Override
    public void paint(Graphics2D g2d) {
        g2d.setColor(getFillColor());
        g2d.fill(getBounds());
        g2d.setColor(getStrokeColor());
        g2d.draw(getBounds());
    }

}

public class OvalDrawable extends AbstractDrawable {

    private Ellipse2D bounds;

    public OvalDrawable(int x, int y, int width, int height, Color strokeColor, Color fillColor) {
        super(strokeColor, fillColor);
        bounds = new Ellipse2D.Double(x, y, width, height);
    }

    @Override
    public Rectangle getBounds() {
        return bounds.getBounds();
    }

    @Override
    public void paint(Graphics2D g2d) {
        g2d.setColor(getFillColor());
        g2d.fill(bounds);
        g2d.setColor(getStrokeColor());
        g2d.draw(bounds);
    }

}

I'd then have a dedicated component which was capable of managing and painting them

Simple Shapes

public class DrawablePane extends JPanel {

    private List<Drawable> drawables;

    public DrawablePane() {
        drawables = new ArrayList<>(25);
    }

    public void add(Drawable drawable) {
        drawables.add(drawable);
        repaint();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        for (Drawable drawable : drawables) {
            drawable.paint(g2d);
        }
        g2d.dispose();
    }

}

Which might be used something like...

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }

        DrawablePane pane = new DrawablePane();
        pane.add(new RectangleDrawable(10, 10, 100, 150, Color.YELLOW, Color.GREEN));
        pane.add(new OvalDrawable(100, 20, 50, 50, Color.MAGENTA, Color.BLUE));

        JFrame frame = new JFrame("Testing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
});