java swing – How to draw Rects one on top of the next using?



Using java swing, I have a horizontal line (acting as the horizon). I want to draw rects, stacked, one on top of the other where the rect with the largest width is at the bottom and the rect with the smallest width is at the top. I also want the rects to stick to the horizon when the user re-sizes the screen. this is the calculation i have so far:

package testing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.List;

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

public class Test extends JFrame {
    private static final int FRAME_WIDTH = 600;
    private static final int FRAME_HEIGHT = 500;

    public static void main(String[] args) {
        new Test();
    }
    
    protected Test() {
        this.setTitle("Tower of Hanoi");
        this.setSize(FRAME_WIDTH, FRAME_HEIGHT);
        this.setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setMinimumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setMaximumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setBackground(Color.BLUE);
        this.add(new PanelTest(), BorderLayout.CENTER);
        this.setVisible(true);
    }
    
    public class PanelTest extends JPanel {

        private static final int FRAME_WIDTH = 600;
        private static final int FRAME_HEIGHT = 500;
         
        protected PanelTest() {
            setLayout(new BorderLayout());
            this.setSize(FRAME_WIDTH, FRAME_HEIGHT);
            this.setMinimumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
            this.setMaximumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
            this.setBackground(Color.CYAN);
            this.add(new ComponentTest(), BorderLayout.CENTER);
            revalidate();
            repaint();
            this.setVisible(true); //probably not necessary

        }
    }
    
    public class ComponentTest extends JComponent {
        private List<Rectangle> rects; 
        
        public ComponentTest() {
            rects = new ArrayList<>(); 
            rects.add(new Rectangle(30, 30, 30, 30));
            rects.add(new Rectangle(30, 30, 30, 30));
            rects.add(new Rectangle(30, 30, 30, 30));
            rects.add(new Rectangle(30, 30, 30, 30));
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D graph2 = (Graphics2D) g;
            graph2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            
            paint(g);
        }
        
        public void paint(Graphics g) {
            // Draw the platform
            g.drawLine(100, getHeight() - 100, getWidth() - 100, getHeight() - 100);
                        
            for(int i = 0; i < rects.size(); i++) {
                rects.get(i).setBounds(getWidth()/2,
                        (30 * i + 1),
                        30 * i + 1,
                        30
                        );
                g.drawRect(rects.get(i).x, rects.get(i).y, rects.get(i).width, rects.get(i).height);
            }
        }
    }
}

this leaves the rects stacked but floating near the top of the screen: enter image description here

how do i get it to stick to the black horizon line? in other words, how do i get the largest rect to rest on top of the black line? furthermore, when the screen is dragged to be a larger size – how can i grow all of the rects heights (equally) and have it still stuck to the black horizon?

also note, the number of rects being drawn is decided by the user (in this example it’s 5, but it could be anything up to 9).

Update doing this also does not work as expected:

    for(int i = 0; i < rects.size(); i++) {
        rects.get(i).setBounds(getWidth()/2,
                (30 * i + 1),
                getHeight() - 100 - (30 * (rects.size() - (i + 1))),
                30
                );
        g.drawRect(rects.get(i).x, rects.get(i).y, rects.get(i).width, rects.get(i).height);
    }

Answer

You don’t need class ComponentTest. You only need class PanelTest and you only need to override method paintComponent() in class PanelTest. Refer to Performing Custom Painting.

If you fix the location of the horizon in PanelTest then you need to draw the rectangles relative to the horizon. The x coordinate is the same for each rectangle as is the height, so you just need to calculate the width (which you are already doing correctly) and the y coordinate.

This means that the y coordinate for the bottom-most rectangle, i.e. the one touching the horizon, needs to be the y coordinate of the horizon minus the height of the rectangle.

The y coordinate of the rectangle directly above the bottom-most rectangle needs to be the y coordinate of the horizon minus twice the height of the rectangle, since each rectangle has the same height.

And so on for the remaining rectangles.

Here is my rewrite of class PanelTest. As I said, no need for class ComponentTest and I made no changes to class Test.

Note that since you are drawing rectangles, using anti-aliasing makes no difference. It makes a difference when you draw curved shapes or diagonal lines. There is also no need for class Graphics2D since you don’t use any of that class’s methods.

public class PanelTest extends JPanel {
    private static final int FRAME_WIDTH = 600;
    private static final int FRAME_HEIGHT = 500;

    private List<Rectangle> rects;

    protected PanelTest() {
        rects = new ArrayList<>();
        rects.add(new Rectangle(30, 30, 30, 30));
        rects.add(new Rectangle(30, 30, 30, 30));
        rects.add(new Rectangle(30, 30, 30, 30));
        rects.add(new Rectangle(30, 30, 30, 30));
        this.setPreferredSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setMinimumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setMaximumSize(new Dimension(FRAME_WIDTH, FRAME_HEIGHT));
        this.setBackground(Color.CYAN);
    }

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

        g.drawLine(100, getHeight() - 100, getWidth() - 100, getHeight() - 100);

        for (int i = 0; i < rects.size(); i++) {
            rects.get(i).x = getWidth() / 2;
            rects.get(i).y = getHeight() - 100 - (30 * (rects.size() - i));
            rects.get(i).width = 30 * i + 1;
            rects.get(i).height = 30;
            g.drawRect(rects.get(i).x, rects.get(i).y, rects.get(i).width, rects.get(i).height);
        }
    }
}


Source: stackoverflow