Jframe - Drawing Rectangles not working

620 Views Asked by At

I have this code which is meant to draw a rectangle over a preloaded image, but it doesn't work.

When i add the drawing class to the frame, it overwrites the image, meaning i can't see my preloaded image, but it still allows me to draw the rectangles.

Also, instead of putting the jframe in the middle of my screen, it puts it in the top right hand corner and i have to maximize it to see the frame.

The code:

public class defineArea {

public static void main(String[] args) throws IOException {

    displayImage();
}

private static void displayImage() throws IOException {

    BufferedImage image = ImageIO.read(new File("C:\\Users\\Rusty\\Desktop\\temp\\Test_PDF-1.png"));
    ImageIcon icon = new ImageIcon(image);
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    JLabel lbl = new JLabel();
    lbl.setIcon(icon);
    JScrollPane jsp = new JScrollPane(lbl);
    frame.add(jsp);
    frame.add(new paintRectangles());
    frame.pack();
    frame.setVisible(true);

}

public static class paintRectangles extends JComponent {
    ArrayList<Shape> shapes = new ArrayList<Shape>();
    Point startDrag, endDrag;

    public paintRectangles() throws IOException {

        this.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                startDrag = new Point(e.getX(), e.getY());
                endDrag = startDrag;
                repaint();
            }

            public void mouseReleased(MouseEvent e) {
                Shape r = makeRectangle(startDrag.x, startDrag.y, e.getX(), e.getY());
                shapes.add(r);
                startDrag = null;
                endDrag = null;
                repaint();
            }
        });

        this.addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseDragged(MouseEvent e) {
                endDrag = new Point(e.getX(), e.getY());
                repaint();
            }
        });
    }

    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Color[] colors = { Color.YELLOW, Color.MAGENTA, Color.CYAN, Color.RED, Color.BLUE, Color.PINK };
        int colorIndex = 0;

        g2.setStroke(new BasicStroke(2));
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50f));

        for (Shape s : shapes) {
            g2.setPaint(Color.BLACK);
            g2.draw(s);
            g2.setPaint(colors[(colorIndex++) % 6]);
            g2.fill(s);
        }

        if (startDrag != null && endDrag != null) {
            g2.setPaint(Color.LIGHT_GRAY);
            Shape r = makeRectangle(startDrag.x, startDrag.y, endDrag.x, endDrag.y);
            g2.draw(r);
            System.out.println(r.getBounds2D());
        }
    }
}

private static Rectangle2D.Float makeRectangle(int x1, int y1, int x2, int y2) {
    return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
}

}

Can anyone help? I'm basically trying to get the 2d Rectangle co-ordinates from the drawn rectangle (as per the system out getbounds2d).

if you remove frame.add(new paintRectangles());, you can see how the frame is meant to look (but without the ability to draw rectangles)

2

There are 2 best solutions below

2
c0der On BEST ANSWER

You could add paintRectangles (which should be PaintRectangles btw) to the JLayeredPane.
The solution is not ideal but it is working.
It worth a test drive to get to know this option, if nothing else :

public class DefineArea {

    public static void main(String[] args) throws IOException {

        displayImage();
    }
    private static void displayImage() throws IOException {

        //  URL url = new URL("http://www.digitalphotoartistry.com/rose1.jpg");
        //    BufferedImage image = ImageIO.read(url);
        //    ImageIcon icon= new ImageIcon(image);

        URL url = DefineArea.class.getResource("image.jpg");
        BufferedImage image = ImageIO.read(url);
        ImageIcon icon = new ImageIcon(image);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JLabel lbl = new JLabel();
        lbl.setIcon(icon);

        JScrollPane jsp = new JScrollPane(lbl);
        frame.add(jsp);

        //add glass pane to layered pane
        JComponent glass = new paintRectangles();
        JLayeredPane lp = frame.getLayeredPane();
        int w = icon.getIconWidth(); int h = icon.getIconHeight();
        // Size is needed here, as there is no layout in lp
        //to make it useful you need to dynamically adjust glass size
        glass.setSize(w,h);
        lp.add(glass);

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

    public static class paintRectangles extends JComponent {
        ArrayList<Shape> shapes = new ArrayList<>();
        Point startDrag, endDrag;

        public paintRectangles() throws IOException {

            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    System.out.println("mousePressed");
                    startDrag = new Point(e.getX(), e.getY());
                    endDrag = startDrag;
                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    Shape r = makeRectangle(startDrag.x, startDrag.y, e.getX(), e.getY());
                    shapes.add(r);
                    startDrag = null;
                    endDrag = null;
                    repaint();
                }
            });

            addMouseMotionListener(new MouseMotionAdapter() {
                @Override
                public void mouseDragged(MouseEvent e) {
                    endDrag = new Point(e.getX(), e.getY());
                    repaint();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            Color[] colors = { Color.YELLOW, Color.MAGENTA, Color.CYAN, Color.RED, Color.BLUE, Color.PINK };
            int colorIndex = 0;

            g2.setStroke(new BasicStroke(2));
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50f));

            for (Shape s : shapes) {
                g2.setPaint(Color.BLACK);
                g2.draw(s);
                g2.setPaint(colors[(colorIndex++) % 6]);
                g2.fill(s);
            }

            if ((startDrag != null) && (endDrag != null)) {
                g2.setPaint(Color.LIGHT_GRAY);
                Shape r = makeRectangle(startDrag.x, startDrag.y, endDrag.x, endDrag.y);
                g2.draw(r);
                System.out.println(r.getBounds2D());
            }
        }
    }

    private static Rectangle2D.Float makeRectangle(int x1, int y1, int x2, int y2) {
        return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2),
                                            Math.abs(x1 - x2), Math.abs(y1 - y2));
    }
}

For more information see How to Use Layered Panes

0
MadProgrammer On

A number of issues jump out.

First, you clearly don't understand how BorderLayout works and what it will do if you try and add two components to the same position.

Second, overriding paint is not a recommend approach to performing custom painting, you should be using a paintComponent and, unless you have a clear understanding of how painting works, call super.paintComponent before you do any custom painting.

Third, you seem to be trying to overlay a component on top of a scroll pane, which, in it's current configuration, will prevent the scroll pane from reacting to mouse events. It also means that if the scroll pane's contents move, what you've painted over the top, won't scroll with it

My "general' recommendation is - read up on the layout managers, get a better understand of how they work. Read up on how the painting system works in Swing, gain a better understanding of how you can work with it to achieve your desired results. Start with a single component which can paint a image itself, then to which you can add you custom painting requirements, this will greatly reduce the issues you're currently having and new issues you will have

Start by having a look at:

A "simple" solution to your immediate problem would be to add you "paint" panel to the JLabel. By default, the JLabel doesn't have a layout manager, so you will need to supply one, for example:

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

public class Test {

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

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                BufferedImage image = ImageIO.read(new File("C:\\Users\\Rusty\\Desktop\\temp\\Test_PDF-1.png"));
                ImageIcon icon = new ImageIcon(image);
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                JLabel lbl = new JLabel();
                lbl.setIcon(icon);

                lbl.setLayout(new BorderLayout());
                lbl.add(new PaintOverlayPane());

                JScrollPane jsp = new JScrollPane(lbl);
                frame.add(jsp);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public class PaintOverlayPane extends JPanel {

        ArrayList<Shape> shapes = new ArrayList<Shape>();
        Point startDrag, endDrag;

        public PaintOverlayPane() {
            setOpaque(false);
            this.addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    startDrag = new Point(e.getX(), e.getY());
                    endDrag = startDrag;
                    repaint();
                }

                public void mouseReleased(MouseEvent e) {
                    Shape r = makeRectangle(startDrag.x, startDrag.y, e.getX(), e.getY());
                    shapes.add(r);
                    startDrag = null;
                    endDrag = null;
                    repaint();
                }
            });

            this.addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseDragged(MouseEvent e) {
                    endDrag = new Point(e.getX(), e.getY());
                    repaint();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            Color[] colors = {Color.YELLOW, Color.MAGENTA, Color.CYAN, Color.RED, Color.BLUE, Color.PINK};
            int colorIndex = 0;

            g2.setStroke(new BasicStroke(2));
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.50f));

            for (Shape s : shapes) {
                g2.setPaint(Color.BLACK);
                g2.draw(s);
                g2.setPaint(colors[(colorIndex++) % 6]);
                g2.fill(s);
            }

            if (startDrag != null && endDrag != null) {
                g2.setPaint(Color.LIGHT_GRAY);
                Shape r = makeRectangle(startDrag.x, startDrag.y, endDrag.x, endDrag.y);
                g2.draw(r);
                System.out.println(r.getBounds2D());
            }
        }
    }

    private static Rectangle2D.Float makeRectangle(int x1, int y1, int x2, int y2) {
        return new Rectangle2D.Float(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2));
    }
}

The short coming of this issue is you have no control over the placement of the image, nor do you have any control over the sizing of the overlay panel, so it would be possible for the label's size to change and all your painting would no longer match the icon.

A better solution would be to paint the image within the same panel. This way you gain complete control over the position of the image and the position of the shapes and can control how they might get offset, if you so desired