title: Relative positioning of Graphics2D objects on JPanel
body: <p>I am making a GUI with Swing and would like to configure the Graphics2D objects with relative positioning.  I want to do this in a way that when I resize the window the objects are repainted in a new location relative to the initial anchor position (probably in the top left corner somewhere).  I have tried using layout managers to do this  but it doesn't impact the actual drawing because the points are pretty much hard coded.  </p>
  
  <p>What is the best way to do this?  Can anyone provide an good example?  Thanks.</p>
  
  <p>Here is my self contained example:</p>
  
  <pre><code>import javax.swing.*;
  import java.awt.*;
  import java.awt.event.ActionEvent;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.awt.geom.Ellipse2D;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Map;
  
  public class DrawPanelMain extends JPanel {
  
      public static final double version = 0.0;
      private JPanel btnPanel = new JPanel();
      private JPanel switchPanel = new JPanel();
      private JScrollPane switchPanelScrollPane = new JScrollPane(switchPanel);
      //private JPanel[] panelArray = new JPanel[3];
  
      DrawEllipses drawEllipses = new DrawEllipses(POINT_LIST);
  
      public static void main(String[] args) {
  
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  initializePointList();
                  createAndShowGui();
              }
          });
      }
  
      public static java.util.List&lt;Point&gt; POINT_LIST = new ArrayList&lt;&gt;();
  
      /*
      * This loop will initialize POINT_LIST with the set of points for drawing the ellipses.
      * The for each loop initializes points for the top row and the second for loop draws the
      * right triangle.
      */
      private static void initializePointList() {
  
          int ellipsePointsYCoordinate[] = {140, 200, 260, 320, 380, 440, 500, 560, 620};
          int ellipsePointsXCoordinate[] = {140, 200, 260, 320, 380, 440, 500, 560, 620, 680};
          int xx = 80;
  
          for (int aXt : ellipsePointsXCoordinate) {
              POINT_LIST.add(new Point(aXt, xx));
          }
  
          for (int i = 0; i &lt; ellipsePointsYCoordinate.length; i++) {
              for (int j = i; j &lt; ellipsePointsYCoordinate.length; j++) {
                  POINT_LIST.add(new Point(ellipsePointsXCoordinate[i], ellipsePointsYCoordinate[j]));
              }
          }
      }
  
      public DrawPanelMain() {
  
          switchPanel.setBorder(BorderFactory.createLoweredSoftBevelBorder());
          switchPanel.setBackground(Color.DARK_GRAY);
          switchPanel.add(drawEllipses);
          switchPanelScrollPane.setPreferredSize(new Dimension(600,600));
  
          setLayout(new GridBagLayout());
          GridBagConstraints c = new GridBagConstraints();
  
          // first column
          c.gridx = 0;
          add(switchPanelScrollPane, c);
  
          // second column
          c.gridx = 1;
  
          // first row
          c.gridy = 0;
  
          // second row
          c.gridy = 1;
          c.gridx = 0;
          add(btnPanel, c);
  
          btnPanel.add(new JButton(new AddSwitchAction("Add Switch Panel")));
      }
  
      public static void createAndShowGui() {
  
          JFrame frame = new JFrame("RF Connection Panel " + version);
  
          frame.setLayout(new BorderLayout());
          frame.add(new DrawPanelMain());
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.setLocationByPlatform(false);
          //frame.setLocationRelativeTo(null);
          frame.pack();
          frame.setVisible(true);
      }
  
      /*
      * AddSwitchAction will add a new pane to the tabbedPane when the add switch button is clicked
      */
      private class AddSwitchAction extends AbstractAction {
          public AddSwitchAction(String name) {
              super(name);
              int mnemonic = (int) name.charAt(0);
              putValue(MNEMONIC_KEY, mnemonic);
          }
  
          @Override
          public void actionPerformed(ActionEvent e) {
              String title = "Switch ";
              DrawEllipses tabComponent = new DrawEllipses(POINT_LIST);
              switchPanel.add(title, tabComponent);
  
          }
      }
  
  }
  
  @SuppressWarnings("serial")
  class DrawEllipses extends JPanel {
      private final int PREF_W = 750; //Window width
      private final int PREF_H = 750; //Window height
      private final int OVAL_WIDTH = 30;
      private static final Color INACTIVE_COLOR = Color.RED;
      private static final Color ACTIVE_COLOR = Color.green;
      private java.util.List&lt;Point&gt; points;
      private java.util.List&lt;Ellipse2D&gt; ellipses = new ArrayList&lt;&gt;();
      private Map&lt;Ellipse2D, Color&gt; ellipseColorMap = new HashMap&lt;&gt;();
  
      /*
       * This method is used to populate "ellipses" with the initialized ellipse2D dimensions
       */
      public DrawEllipses(java.util.List&lt;Point&gt; points) {
          this.points = points;
          for (Point p : points) {
              int x = p.x - OVAL_WIDTH / 2;
              int y = p.y - OVAL_WIDTH / 2;
              int w = OVAL_WIDTH;
              int h = OVAL_WIDTH;
              Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
              ellipses.add(ellipse);
              ellipseColorMap.put(ellipse, INACTIVE_COLOR);
          }
  
          MyMouseAdapter mListener = new MyMouseAdapter();
          addMouseListener(mListener);
          addMouseMotionListener(mListener);
      }
  
      /*
       * paintComponent is used to paint the ellipses
       */
      @Override
      protected void paintComponent(Graphics g) {
          super.paintComponent(g); // paints the background
          setBackground(Color.DARK_GRAY);
          Graphics2D g2 = (Graphics2D) g;
          g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);
          for (Ellipse2D ellipse : ellipses) {
              g2.setColor(ellipseColorMap.get(ellipse));
              g2.fill(ellipse);
              g2.setColor(Color.BLACK);
              g2.setStroke(new BasicStroke(2));
              g2.draw(ellipse);
          }
  
          /*
           * Set the font characteristics, color, and draw the row labels.
           */
          g.setFont(new Font("TimesRoman", Font.BOLD, 18));
          g.setColor(Color.BLACK);
  
          //Along the top row
          g.drawString("External Port", 10, 50);
          g.drawString("1", 135, 50);
          g.drawString("2", 195, 50);
          g.drawString("3", 255, 50);
          g.drawString("4", 315, 50);
          g.drawString("5", 375, 50);
          g.drawString("6", 435, 50);
          g.drawString("7", 495, 50);
          g.drawString("8", 555, 50);
          g.drawString("9", 615, 50);
          g.drawString("10", 672, 50);
  
          //Along the Y-axis
          g.drawString("Radio 2", 40, 145);
          g.drawString("3", 90, 205);
          g.drawString("4", 90, 265);
          g.drawString("5", 90, 325);
          g.drawString("6", 90, 385);
          g.drawString("7", 90, 445);
          g.drawString("8", 90, 505);
          g.drawString("9", 90, 565);
          g.drawString("10", 90, 625);
  
          //Along the X-Axis
          g.drawString("1", 135, 670);
          g.drawString("2", 195, 670);
          g.drawString("3", 255, 670);
          g.drawString("4", 315, 670);
          g.drawString("5", 375, 670);
          g.drawString("6", 435, 670);
          g.drawString("7", 495, 670);
          g.drawString("8", 555, 670);
          g.drawString("9", 615, 670);
  
          //Draws a 3DRect around the top row of ellipse2D objects
          g2.setColor(Color.lightGray);
          g2.draw3DRect(120, 60, 580, 40, true);
          g2.draw3DRect(121, 61, 578, 38, true);
          g2.draw3DRect(122, 62, 576, 36, true);
  
      }
  
      /*
       * MouseAdapter is extended for mousePressed Event that detects if the x, y coordinates
       * of a drawn ellipse are clicked.  If the color is INACTIVE it is changed to ACTIVE and
       * vice versa.
       */
      private class MyMouseAdapter extends MouseAdapter {
          @Override
          /*
           * When mousePressed event occurs, the color is toggled between ACTIVE and INACTIVE
           */
          public void mousePressed(MouseEvent e) {
              Color c;
              for (Ellipse2D ellipse : ellipses) {
                  if (ellipse.contains(e.getPoint())) {
                      c = (ellipseColorMap.get(ellipse) == INACTIVE_COLOR) ? ACTIVE_COLOR : INACTIVE_COLOR;
                      ellipseColorMap.put(ellipse, c);
                  }
              }
              repaint();
          }
      }
  
      /*
  * This method will set the dimensions of the JFrame equal to the preferred H x W
  */
      @Override
      public Dimension getPreferredSize() {
          if (isPreferredSizeSet()) {
              return super.getPreferredSize();
          }
          return new Dimension(PREF_W, PREF_H);
      }
  
  
      /*
       * Used for button click action to change all ellipses to ACTIVE_COLOR
       */
      public void activateAll(){
          for (Ellipse2D ellipse : ellipses){
              ellipseColorMap.put(ellipse, ACTIVE_COLOR);
          }
          repaint();
      }
  
      /*
       * Used for button click action to change all ellipses to INACTIVE_COLOR
       */
      public void deactivateAll(){
          for (Ellipse2D ellipse : ellipses){
              ellipseColorMap.put(ellipse, INACTIVE_COLOR);
          }
          repaint();
      }
  }
  </code></pre>