0

I´m really in need of some help from you guys. I try to add rectangles to an ArrayList and then loop through the list to draw them all. I'm not sure if I'm using the right approach, but here is my code so far.

Edit: the code does not draw the rectangles that I added to the ArrayList. I don't even know if they are added the right way, or accessed through the for-loop in the right way.

In TestProgram

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.JFrame;


public class TestProgram extends JFrame {
    private ShapeRectangle rectangle;
    public ArrayList<Shapes> list = new ArrayList<Shapes>();


    public TestProgram(String title) {
        super(title);
        setLayout(new BorderLayout());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        initComponents();
        setSize(500, 700);
        setVisible(true);
  }

    private void initComponents() {
        rectangle = new ShapeRectangle();    
        add(rectangle, BorderLayout.CENTER);

        list.add(rectangle);        
        Graphics g = getGraphics();
        for (int i = 0; i < list.size(); i++) {
            rectangle.draw(g);
        }
  }

    public static void main(String args[]) {
        new TestProgram("Drawing program");
    }
}

In class ShapeRectangles:

import java.awt.Graphics;

public class ShapeRectangle extends Shapes {

    public ShapeRectangle() {}    

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponents(g);
        draw(g);        
    }

    @Override
    public void draw(Graphics g) {
       g.drawLine(20,20,60,60);
       g.drawLine(130,30,80,11);
       g.drawRect(200,30,20,140);        
   }
}

In class Shapes:

import java.awt.Graphics;
import javax.swing.JPanel;

public abstract class Shapes extends JPanel {

    abstract public void draw(Graphics g); 

}

user2939293
  • 793
  • 5
  • 16
  • 41

3 Answers3

2

Don't make Shapes a JPanel. Also take out its paintComponent method.

Instead have a JPanel class which is the main drawing surface. Keep the List<Shapes> in the class. Iterate through the list in the paintComponent method of that class, calling each Shapes's draw method

class DrawingPanel extends JPanel {
    List<Shapes> shapes;
    // add shapes

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (Shapes shape: shapes) {
            shape.draw(g);
        }
    }
}

Then just add that panel to the frame.

Remember that all painting should be done with the Graphics context passed into the paintComponent method. We do not do custom painting by using getGraphics.

Also you'll probably want to store some state in the Shapes class so you can change the x/y/width/height state. Right now you are are using the same hard coded values for all your shapes.

abstract class Shapes {
    int x, y;
    public Shapes(int x, int y) {
        this.x = x; this.y = y;
    }
    protected abstract void draw(Graphics g);
}
class RectangleShape extends Shape {
    int width, height;
    public RectangleShape(int x, int y, int width, int height) {
        super(x, y);
        this.width = width; this.height = height;
    }
    @Override   
    public void draw(Graphics g) {
        g.fillRect(x, y, width, height);
    }
}

See a complete example with more complex objects with animation

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • can you please explain a Little more. I'm new to this and does not understand what you mean. How do I "just add the panel to the frame"? What's in the draw-method? What do you mean by "take out its paintcomponent method"? Thank you for your time and effort – user2939293 Oct 29 '14 at 19:28
  • Add `DrawingPanel` to the `JFrame`. `add(new DrawingPanel())` – Paul Samsotha Oct 29 '14 at 19:31
2

This is bad, you either want to add it as a component with absolute positioning, or you want to draw it, choose one.

this.add(rectangle, BorderLayout.CENTER); // Add as component of the panel.
list.add(rectangle);                      // Add as method of drawing on the panel.

The latter will work better because you are drawing. If you need drag-and-drop, add it as a child, but add the drawing to the child.

You can call repaint() on the JFrame to update the graphics after changing the rectangle's coordinates and sizes.

DrawShapes

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class DrawShapes extends JFrame {
    public ArrayList<Shape> shapeList = new ArrayList<Shape>();

    public DrawShapes(String title) {
        super(title);

        this.setLayout(new BorderLayout());
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setSize(500, 700);
        this.setLocationRelativeTo(null);

        this.initComponents();
    }

    private void initComponents() {
        shapeList.add(new RectangleShape(20, 20, 60, 60));
        shapeList.add(new RectangleShape(130, 30, 80, 11));
        shapeList.add(new RectangleShape(200, 30, 20, 140));
    }

    @Override
    public void paint(Graphics g) {
        for (Shape s : shapeList) {
            s.draw(g);
        }
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                DrawShapes d = new DrawShapes("Drawing program");
                d.setVisible(true);
            }
        });
    }
}

RectangleShape

import java.awt.Graphics;

public class RectangleShape extends Shape {
    public RectangleShape(int x, int y, int width, int height) {
        super(x, y, width, height);
    }

    public RectangleShape() {
        super();
    }

    @Override
    public void draw(Graphics g) {
        g.drawRect(getX(), getY(), getWidth(), getHeight());
    }
}

Shape

import java.awt.Graphics;

public abstract class Shape {
    private int x;
    private int y;
    private int width;
    private int height;

    public Shape() {
        this(0, 0, 1, 1);
    }

    public Shape(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    public abstract void draw(Graphics g);

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • This works, thank you so much. I'm actually supposed to have two more classes ShapesCircles and ShapesFreeHand. I'm also supposed to draw the rectangles, circles and FreeHands by dragging the mouse on the screen. My next challenge... You wrote "If you need drag-and-drop, add it as a child, but add the drawing to the child." What do you mean by that? Any suggestions of how I can fix it? Thank you again! – user2939293 Oct 29 '14 at 20:01
  • Well, for rectangle, if you end up making it a `JComponent` again, the draw method will have to ignore global `x` and `y`. You will start from "`(0, 0)`". Change `g.drawRect(getX(), getY(), getWidth(), getHeight());` to `g.drawRect(0, 0, getWidth(), getHeight());` `getWidth()` and `getHeight()` will be provided by the `JComponent`/`JPanel`. – Mr. Polywhirl Oct 29 '14 at 20:04
  • I'm not sure if I understand what you mean. How can I provide getHeight() and getHeight() from JPanel? I'm sorry if I sound stupid but this is really hard for me. – user2939293 Oct 29 '14 at 20:09
  • Can you also explain to me where I should put the addMouseListener()? Doesn't it take an object as an argument, but I haven't created any object. – user2939293 Oct 29 '14 at 20:39
  • All non-primatives in Java are Objects – Mr. Polywhirl Oct 29 '14 at 20:47
  • Ok, but how can I make the program get the x and y-coordinates and pass it to the constructor? I know I can use e.getX(); and e.getY(); but don't I get them from the mousePressed-method INSIDE ShapeRectangle? I need them to pass them to the constructor, so what can I do to solve this problem? Thank you, I really appreciate your help – user2939293 Oct 29 '14 at 20:52
  • Is this the right way to add the mouseListener in class DrawShapes: ShapeRectangle rec = new ShapeRectangle(100, 200, 60, 60); shapeList.add(rec); addMouseListener(rec); – user2939293 Oct 29 '14 at 20:55
  • You may want to pose a new question. This is too much. Take a look at: [Drawing a JComponent inside a JPanel](http://stackoverflow.com/questions/12873056/drawing-a-jcomponent-inside-a-jpanel). Do not turn this comment stream into a discussion. Thanks. – Mr. Polywhirl Oct 29 '14 at 21:02
  • Ok, I might do that. Thank you again! – user2939293 Oct 29 '14 at 21:05
2

Start by taking a look at the 2D Graphics, in particular Working with Geometry

The 2D Graphics API defines a Shape API which includes, amongst other things, a Rectangle class

For example...

enter image description here

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestRectangles {

    public static void main(String[] args) {
        new TestRectangles();
    }

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

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

    public class TestPane extends JPanel {

        private List<Rectangle> rectangles;

        public TestPane() {
            rectangles = new ArrayList<>(25);
            Random ran = new Random();

            for (int index = 0; index < 10; index++) {
                int x = ran.nextInt(200 - 10);
                int y = ran.nextInt(200 - 10);
                int width = ran.nextInt(200 - x);
                int height = ran.nextInt(200 - y);
                rectangles.add(new Rectangle(
                        x, 
                        y, 
                        width, 
                        height));
            }
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            for (Rectangle rect : rectangles) {
                g2d.draw(rect);
            }
            g2d.dispose();
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366