Olavi Mustanoja
Olavi Mustanoja

Reputation: 2065

Rounded borders when parent object has a background image

I'm trying to make a border with round corners. Inside the borders should be anything that the component, for which the border is set, decides to draw, and outside the borders should "nothing"; that is, it should draw the parent component's paint in those places.

What I'm trying to get:

enter image description here

What I'm getting:

enter image description here

See the white corners of the container with blue borders. I need to get rid of them. I'm trying to achieve this with a custom Border :

public class RoundedLineBorder extends AbstractBorder {

    @Override
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
        Graphics2D g2 = (Graphics2D) g;            
        int arc = 20;

        RoundRectangle2D borderRect = new RoundRectangle2D.Double(
                0, 0, width - 1, height - 1, arc, arc);
        Rectangle fullRect = new Rectangle(
                0, 0, width, height);

        Area borderArea = new Area(borderRect);
        Area parentArea = new Area(fullRect);
        parentArea.subtract(borderArea);

        Component parent = c.getParent();
        if (parent != null) {
            g2.setColor(parent.getBackground());

            /* fill parent background color outside borders */
            g2.setClip(parentArea);
            g2.fillRect(fullRect);

            g2.setClip(null);
        }

        g2.setColor(Color.blue);

        /* draw borders */
        g2.draw(borderArea);
    }

}

This works fine when the parent component has a solid background, but if it has a background image it of course doesn't. Is there a way of getting the actual colours that are painted under the aforementioned places?

Is there a better way of achieving rounded borders without actually extending a JPanel and just doing it all in its paintComponent?

Upvotes: 0

Views: 454

Answers (1)

camickr
camickr

Reputation: 324157

it requires a tight coupling between the border and the component,

You could create a container component to do this so you don't need to customize every component.

Something like:

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;

public class RoundedBorderContainer extends JPanel implements Border
{
    private boolean componentWasOpaque;

    public RoundedBorderContainer(JComponent component)
    {
        setLayout( new BorderLayout() );
        add( component );

        componentWasOpaque = component.isOpaque();
        component.setOpaque( false );
        setOpaque( false );

        setBorder( this );
    }

    @Override
    public void paint(Graphics g)
    {
        Graphics2D g2 = (Graphics2D) g;
        int arc = 20;
        int width = getWidth();
        int height = getHeight();

        RoundRectangle2D borderRect = new RoundRectangle2D.Double(0, 0, width, height, arc, arc);
        g2.setClip(borderRect);

        super.paint(g);
        super.paintBorder(g);

        g2.setClip( null );
    }

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

        if (componentWasOpaque)
        {
            g.setColor(getComponent(0).getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    @Override
    public Insets getBorderInsets(Component c)
    {
        return new Insets(1, 1, 1, 1);
    }

    @Override
    public boolean isBorderOpaque()
    {
        return false;
    }

    @Override
    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
    {
        Graphics2D g2 = (Graphics2D) g;

        int arc = 20;
        RoundRectangle2D borderRect = new RoundRectangle2D.Double(0, 0, width - 1, height - 1, arc, arc);
        Rectangle fullRect = new Rectangle(0, 0, width, height);

        Area borderArea = new Area(borderRect);
        borderArea = new Area(borderRect);
        Area parentArea = new Area(fullRect);

        g2.setColor(Color.RED);

        g2.draw(borderArea);
    }

    private static void createAndShowGUI()
    {
        JLabel label = new JLabel( new ImageIcon("grass.jpg") );
        label.setLayout( new GridBagLayout() );

        JPanel panel = new JPanel();
        panel.setPreferredSize( new Dimension(100, 100) );
        panel.setBackground( Color.BLACK );

//        JLabel panel = new JLabel( new ImageIcon("???") );

        RoundedBorderContainer rbc = new RoundedBorderContainer(panel);
        label.add(rbc);

        JFrame frame = new JFrame("Rounded Border");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( label );
        frame.setLocationByPlatform( true );
//        frame.pack();
        frame.setSize(400, 400);
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}

Rounded Border

Upvotes: 1

Related Questions