2

I have been having a some issues with Swing lately. I'm trying to make something like in the image below on the fly, to illustrate the data structures for algorithms.

notebook image
(source: ius.edu)

All that I was trying to do in the following class is draw out some rectangles with numbers on them. and translate them, however the last rectangle draws at 0,0. I'm stumped.

If you add the JPanel (commented out) after the loop then it draws as expected.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class DrawingRect {

    public static void main(String[] args) {
        DrawingRect d = new DrawingRect();
    }

    public DrawingRect() {

        JFrame frame = new JFrame("Drawing a rect");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel swingPanel = new JPanel();
        swingPanel.setPreferredSize(new Dimension(500, 500));

        swingPanel.setVisible(true);

        swingPanel.setLayout(new BorderLayout(0, 0));

        int base = 15;
        for (int i = 1; i <= 25; i++) {
            Graphic re = new Graphic(i);

            //translating the graphic
            re.setBounds(base + 30 * i, base + 20 * i, 110, 110);


            swingPanel.add(re);
        }


        // if say I add a JPanel in here as the last element
        // then the boxes will draw correctly.
        //swingPanel.add(new JPanel());

        swingPanel.setPreferredSize(new Dimension(800, 600));

        frame.getContentPane().add(swingPanel);

        frame.pack();
        frame.setVisible(true);
    }

    public class Graphic extends JComponent {

        private static final long serialVersionUID = 1L;
        private static final int PREF_W = 100;
        private static final int PREF_H = 100;
        int id;

        public Graphic(int id) {
            this.id = id;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2 = (Graphics2D) g;
            g2.setColor(Color.black);

            g2.setColor(Color.black);
            g2.drawRoundRect(0, 0, 30, 30, 20, 20);

            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

            Font font = new Font("", Font.PLAIN, 13);
            g2.setFont(font);

            g2.drawString("" + id, 15, 20);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(PREF_W, PREF_H);
        }
    }
}   
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Andrew
  • 1,764
  • 3
  • 24
  • 38

2 Answers2

3

The default layout of JPanel is FlowLayout, but you're positioning components as if the layout were null. Moreover, you're drawing content that a nested component could do automatically.

Instead, add several JPanels containing JLabel's to a containing panel having GridLayout. Use empty panels as required. Override paintComponent() in the outer panel to draw connecting lines.

Addendum: Anytime one is tempted to use Absolute Positioning, JInternalFrame may be an alternative. Related examples may be found here and here.

Addendum: If the project grows beyond the prototype stage, also consider a Custom Layout Manager.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • 1
    Thanks setting the layout to null gives me the result that I'm after, I am still using the JPanel however. – Andrew Apr 13 '12 at 22:18
2

It is because you are using BorderLayout. You can test this by changing your add line to include a specific layout location: swingPanel.add(re,BorderLayout.SOUTH).

As for a solution, why not draw all the rectangles on a picture and update it at the end of the loop? Or is the adding to JPanel part of your demonstration?

Youssef G.
  • 617
  • 4
  • 10
  • The `frame` has `BorderLayout` by default, but `swingPanel` _et infra_ have the default `FlowLayout`. +1 for suggesting offscreen composition. – trashgod Apr 13 '12 at 21:25
  • @trashgod, could you please explain how the frame is the one with Borderlayout and the Panel is the one with FlowLayout? I don't see a layout being set on the Frame, so I assume it has the default one, but the swingPanel is clearly being set to `BorderLayout`. Changing or removing `swingPanel.setLayout(new BorderLayout());` shows the expected flow layout where everything is in a row and wraps to a new row when no more space is available. – Youssef G. Apr 14 '12 at 13:29
  • Oops, my error; your analysis is correct. I focused on the no-arg constructor `new JPanel()` and overlooked the subsequent `setLayout()`. Thank you for clarifying. – trashgod Apr 14 '12 at 16:53