Drawing multiple graphic2d components into JPanel

4.7k views Asked by At

I've read a lot of tutorials on drawing Graphics2D components and adding to JPanel/JFrame but I can't find how to add multiple these components into one JPanel simply. My code below adds only 1 component (line) and I can't find why it isn't possible to add more.

What am I doing wrong?

enter image description here

Desired behaviour: there should be 3 red lines.

My whole code:

package Examples;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Example1 extends JFrame {

    private final JPanel panel;

    public Example1() {

        // jpanel with graphics
        panel = new JPanel();
        panel.setPreferredSize(new Dimension(200, 200));
        panel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        panel.setBackground(Color.WHITE);
        add(panel);


        // adding lines to jpanel
        AddMyLine(); // 1st: this works well
        AddMyLine(); // 2nd: this doesn't work
        AddMyLine(); // 3rd: this doesn't work


        setLayout(new FlowLayout(FlowLayout.LEFT));
        setSize(250, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        setLocationRelativeTo(null);
    }


    // add new line to jpanel
    private void AddMyLine() {
        MyLine c = new MyLine();
        System.out.println(c);
        panel.add(c);
    }



    // line component
    private class MyLine extends JComponent {

        public MyLine() {
            setPreferredSize(new Dimension(200, 200));
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D)g;
            g2d.setColor(Color.red);
            g2d.setStroke(new BasicStroke(1));
            int x1 = (int)Math.round(Math.random()*200);
            int y1 = (int)Math.round(Math.random()*200);
            int x2 = (int)Math.round(Math.random()*200);
            int y2 = (int)Math.round(Math.random()*200);
            g2d.drawLine(x1,y1,x2,y2);
        }
    }


    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Example1();
            }
        });     

    }
}
3

There are 3 answers

0
Hovercraft Full Of Eels On BEST ANSWER

Your MyLine class should not be a Swing component and thus should not extend JComponent. Rather it should be a logical entity, and in fact can be something that implements Shape such as a Line2D, or could be your own complete class, but should know how to draw itself, i.e., if it does not implement Shape, then it should have some type of draw(Graphics2D g) method that other classes can call. I think instead you should work on extending your panel's JPanel class, such that you override its paintComponent method, give it a collection to hold any MyLine items added to it, and draw the MyLine items within the paintComponent.

Other options include drawing directly on to a BufferedImage, and then displaying that BufferedImage in your JPanel's paintComponent method. This is great for static images, but not good for images that need to change or move.

e.g.,

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

public class DrawChit extends JPanel {
   private static final int PREF_W = 500;
   private static final int PREF_H = PREF_W;
   private List<Shape> shapes = new ArrayList<>();

   public DrawChit() {
      setBackground(Color.white);
   }

   public void addShape(Shape shape) {
      shapes.add(shape);
      repaint();
   }

   @Override // make it bigger
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.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);
      for (Shape shape : shapes) {
         g2.draw(shape);
      }
   }

   private static void createAndShowGui() {
      DrawChit drawChit = new DrawChit();

      drawChit.addShape(new Line2D.Double(10, 10, 100, 100));
      drawChit.addShape(new Ellipse2D.Double(120, 120, 200, 200));

      JFrame frame = new JFrame("DrawChit");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(drawChit);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

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

Or an example using my own MyDrawable class, which produces a GUI that looks like this:

enter image description here

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class DrawChit extends JPanel {
   private static final int PREF_W = 600;
   private static final int PREF_H = PREF_W;
   private List<MyDrawable> drawables = new ArrayList<>();

   public DrawChit() {
      setBackground(Color.white);
   }

   public void addMyDrawable(MyDrawable myDrawable) {
      drawables.add(myDrawable);
      repaint();
   }

   @Override
   // make it bigger
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.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);
      for (MyDrawable myDrawable : drawables) {
         myDrawable.draw(g2);
      }
   }

   public void clearAll() {
      drawables.clear();
      repaint();
   }

   private static void createAndShowGui() {
      final List<MyDrawable> myDrawables = new ArrayList<>();
      myDrawables.add(new MyDrawable(new Line2D.Double(100, 40, 400, 400),
            Color.red, new BasicStroke(40)));
      myDrawables.add(new MyDrawable(new Ellipse2D.Double(50, 10, 400, 400),
            Color.blue, new BasicStroke(18)));
      myDrawables.add(new MyDrawable(new Rectangle2D.Double(40, 200, 300, 300),
            Color.cyan, new BasicStroke(25)));
      myDrawables.add(new MyDrawable(new RoundRectangle2D.Double(75, 75, 490, 450, 40, 40),
            Color.green, new BasicStroke(12)));

      final DrawChit drawChit = new DrawChit();

      JFrame frame = new JFrame("DrawChit");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(drawChit);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      int timerDelay = 1000;
      new Timer(timerDelay, new ActionListener() {
         private int drawCount = 0;

         @Override
         public void actionPerformed(ActionEvent e) {
            if (drawCount >= myDrawables.size()) {
               drawCount = 0;
               drawChit.clearAll();
            } else {
               drawChit.addMyDrawable(myDrawables.get(drawCount));
               drawCount++;
            }
         }
      }).start();
   }

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

class MyDrawable {
   private Shape shape;
   private Color color;
   private Stroke stroke;

   public MyDrawable(Shape shape, Color color, Stroke stroke) {
      this.shape = shape;
      this.color = color;
      this.stroke = stroke;
   }

   public Shape getShape() {
      return shape;
   }

   public Color getColor() {
      return color;
   }

   public Stroke getStroke() {
      return stroke;
   }

   public void draw(Graphics2D g2) {
      Color oldColor = g2.getColor();
      Stroke oldStroke = g2.getStroke();

      g2.setColor(color);
      g2.setStroke(stroke);
      g2.draw(shape);

      g2.setColor(oldColor);
      g2.setStroke(oldStroke);
   }

   public void fill(Graphics2D g2) {
      Color oldColor = g2.getColor();
      Stroke oldStroke = g2.getStroke();

      g2.setColor(color);
      g2.setStroke(stroke);
      g2.fill(shape);

      g2.setColor(oldColor);
      g2.setStroke(oldStroke);
   }

}
0
DJClayworth On

You shouldn't be drawing lines by adding components. Components are things like panels, buttons etc.

See this tutorial on how to draw with Graphics2D: https://docs.oracle.com/javase/tutorial/2d/geometry/primitives.html

0
Mohammad Alavi On

Your code adds three components but the panel is not big enough to show the other two components

    panel.setPreferredSize(new Dimension(200, 600));
    setSize(250, 800);