How to draw a Round rectangle in java with normal rectangle outline

user2016175 picture user2016175 · Jan 27, 2013 · Viewed 21.5k times · Source

For my java application i need a Round rectangle with an outline that looks like a normal rectangle, like this

enter image description here

I know you can do that by drawing a normal rectangle and a RoundRect inside it but i don't want to draw a RoundRect inside it because I want to draw something else in it.

So a round rect with normal corners. How do I draw that in Java?

The problem is that the rectangle looks like this if I use layers: enter image description hereThe corners are filled up with the wrong color. How do I prevent that?

Answer

MadProgrammer picture MadProgrammer · Jan 28, 2013

I can think of two approaches. The first is to generate a Shape that represents the square outter edge and the rounded inner edge.

The second would be to use a AlphaComposite to generate a masked result.

enter image description here

public class TestMask {

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

    public TestMask() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new MaskedPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class MaskedPane extends JPanel {

        public MaskedPane() {
            setBackground(Color.RED);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

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

            BufferedImage outter = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = outter.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.BLACK);
            g2d.fillRect(0, 0, getWidth(), getHeight());
            g2d.dispose();
            BufferedImage inner = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
            g2d = inner.createGraphics();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.BLACK);
            g2d.fillRoundRect(10, 10, getWidth() - 20, getHeight() - 20, 20, 20);
            g2d.dispose();

            BufferedImage masked = applyMask(outter, inner, AlphaComposite.DST_OUT);
            g.drawImage(masked, 0, 0, this);

        }

        public BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage, int method) {
            BufferedImage maskedImage = null;
            if (sourceImage != null) {

                int width = maskImage.getWidth();
                int height = maskImage.getHeight();

                maskedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                Graphics2D mg = maskedImage.createGraphics();

                int x = (width - sourceImage.getWidth()) / 2;
                int y = (height - sourceImage.getHeight()) / 2;

                mg.drawImage(sourceImage, x, y, null);
                mg.setComposite(AlphaComposite.getInstance(method));
                mg.drawImage(maskImage, 0, 0, null);
                mg.dispose();
            }

            return maskedImage;
        }

        public BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage) {
            return (BufferedImage) applyMask(sourceImage, maskImage, AlphaComposite.DST_IN);
        }
    }
}

Updated with Shape example

enter image description here

Finally had time to bang one out...

public class TestMask {

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

    public TestMask() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new ShapedPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ShapedPane extends JPanel {

        public ShapedPane() {
            setBackground(Color.GREEN);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

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

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.BLACK);
            g2d.fill(new RounedFrame(getWidth(), getHeight(), 10, 20));
            g2d.dispose();
        }
    }

    public class RounedFrame extends Path2D.Float {

        public RounedFrame(float width, float height, float thickness, float radius) {

            moveTo(0, 0);
            lineTo(width, 0);
            lineTo(width, height);
            lineTo(0, height);
            lineTo(0, 0);

            float innerWidth = width - thickness;
            float innerHeight = height - thickness;

            moveTo(thickness + radius, thickness);
            lineTo(innerWidth - radius, thickness);

            curveTo(innerWidth, thickness, innerWidth, thickness, innerWidth, thickness + radius);

            lineTo(innerWidth, innerHeight - radius);
            curveTo(innerWidth, innerHeight, innerWidth, innerHeight, innerWidth - radius, innerHeight);
            lineTo(thickness + radius, innerHeight);
            curveTo(thickness, innerHeight, thickness, innerHeight, thickness, innerHeight - radius);
            lineTo(thickness, thickness + radius);
            curveTo(thickness, thickness, thickness, thickness, thickness + radius, thickness);

            closePath();

            setWindingRule(WIND_EVEN_ODD);

        }
    }
}

Updated

From a comment by Andrew, you could simplify the use of the shape example by using Area

You could replace the paintComponent from the above example with this one...

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

    Area area = new Area(new Rectangle(0, 0, getWidth(), getHeight()));
    area.subtract(new Area(new RoundRectangle2D.Float(10, 10, getWidth() - 20, getHeight() - 20, 20, 20)));

    Graphics2D g2d = (Graphics2D) g.create();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setColor(Color.BLACK);
    g2d.fill(area);
    g2d.dispose();
}

Which is much simpler :D