Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Border with rounded corners & transparency

The following screenshot shows a test of TextBubbleBorder1. I would like to make the corners of the component that are outside the rectangle to be entirely transparent & show whatever component is beneath it. I found a way to restrict the BG color of a label to 'inside the border' by setting a Clip (representing the area outside the rounded corners) on the Graphics2D instance and calling clearRect(). That can be seen in Label 1.

Border Test

However you can see the downside of this approach when there is a red BG (or any non-standard color) on the parent panel. The corners default to the default panel color (easiest to see in Panel 2).

Ultimately I would like this to work for a non-standard color in the parent container, but it was partly inspired by What do I need to do to replicate this component with gradient paint?

Does anybody know a way to get those corners transparent?

import java.awt.*; import java.awt.geom.*; import javax.swing.*; import javax.swing.border.*;  public class BorderTest {      public static void main(String[] args) {         Runnable r = new Runnable() {              @Override             public void run() {                 JPanel gui = new JPanel(new GridLayout(1,0,5,5));                 gui.setBorder(new EmptyBorder(10,10,10,10));                 gui.setBackground(Color.RED);                  AbstractBorder brdr = new TextBubbleBorder(Color.BLACK,2,16,0);                  JLabel l1 = new JLabel("Label 1");                 l1.setBorder(brdr);                 gui.add(l1);                  JLabel l2 = new JLabel("Label 2");                 l2.setBorder(brdr);                 l2.setBackground(Color.YELLOW);                 l2.setOpaque(true);                 gui.add(l2);                  JPanel p1 = new JPanel();                 p1.add(new JLabel("Panel 1"));                 p1.setBorder(brdr);                 p1.setOpaque(false);                 gui.add(p1);                  JPanel p2 = new JPanel();                 p2.add(new JLabel("Panel 2"));                 p2.setBorder(brdr);                 gui.add(p2);                  JOptionPane.showMessageDialog(null, gui);             }         };         // Swing GUIs should be created and updated on the EDT         // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html         SwingUtilities.invokeLater(r);     }  }  class TextBubbleBorder extends AbstractBorder {      private Color color;     private int thickness = 4;     private int radii = 8;     private int pointerSize = 7;     private Insets insets = null;     private BasicStroke stroke = null;     private int strokePad;     private int pointerPad = 4;     RenderingHints hints;      TextBubbleBorder(             Color color) {         new TextBubbleBorder(color, 4, 8, 7);     }      TextBubbleBorder(             Color color, int thickness, int radii, int pointerSize) {         this.thickness = thickness;         this.radii = radii;         this.pointerSize = pointerSize;         this.color = color;          stroke = new BasicStroke(thickness);         strokePad = thickness / 2;          hints = new RenderingHints(                 RenderingHints.KEY_ANTIALIASING,                 RenderingHints.VALUE_ANTIALIAS_ON);          int pad = radii + strokePad;         int bottomPad = pad + pointerSize + strokePad;         insets = new Insets(pad, pad, bottomPad, pad);     }      @Override     public Insets getBorderInsets(Component c) {         return insets;     }      @Override     public Insets getBorderInsets(Component c, Insets insets) {         return getBorderInsets(c);     }      @Override     public void paintBorder(             Component c,             Graphics g,             int x, int y,             int width, int height) {          Graphics2D g2 = (Graphics2D) g;          int bottomLineY = height - thickness - pointerSize;          RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(                 0 + strokePad,                 0 + strokePad,                 width - thickness,                 bottomLineY,                 radii,                 radii);          Polygon pointer = new Polygon();          // left point         pointer.addPoint(                 strokePad + radii + pointerPad,                 bottomLineY);         // right point         pointer.addPoint(                 strokePad + radii + pointerPad + pointerSize,                 bottomLineY);         // bottom point         pointer.addPoint(                 strokePad + radii + pointerPad + (pointerSize / 2),                 height - strokePad);          Area area = new Area(bubble);         area.add(new Area(pointer));          g2.setRenderingHints(hints);          Area spareSpace = new Area(new Rectangle(0, 0, width, height));         spareSpace.subtract(area);         g2.setClip(spareSpace);         g2.clearRect(0, 0, width, height);         g2.setClip(null);          g2.setColor(color);         g2.setStroke(stroke);         g2.draw(area);     } } 
  1. While the TextBubbleBorder was devised for Internal padding for JTextArea with background Image (& ended up using a JLabel since the text area was a mess for the reasons mentioned above), by specifying a pointerSize of 0 we end up with a 'rounded rectangle' instead.

like image 318
Andrew Thompson Avatar asked Feb 22 '13 13:02

Andrew Thompson


People also ask

How do you round a border radius?

How to Make a Perfect Circle With a Border Radius in CSS. Add the HTML element. Assign it an equal width and height. Set the CSS border-radius property to 50%.

How do you make a circular border in CSS?

To create a circle we can set the border-radius on the element. This will create curved corners on the element. If we set it to 50% it will create a circle. If you set a different width and height we will get an oval instead.


2 Answers

N.B. There is a clipping bug in this code, which is fixed in the accepted answer to paintComponent() is drawing on other components. This should only be considered as a solution if the 'clipping bug fix' is incorporated.


// Paint the BG color of the parent, everywhere outside the clip // of the text bubble. 

See this point in the code for the source that shows correctly as:

BorderTest with 0px speech pointer

BorderTest with 16px speech pointer

import java.awt.*; import java.awt.image.*; import java.awt.geom.*; import javax.swing.*; import javax.swing.border.*;  public class BorderTest {      public static void main(String[] args) {         Runnable r = new Runnable() {              @Override             public void run() {                 JPanel gui = new JPanel(new GridLayout(2,0,5,5));                 gui.setBorder(new EmptyBorder(10,10,10,10));                 gui.setBackground(Color.RED);                  AbstractBorder brdrLeft = new TextBubbleBorder(Color.BLACK,2,16,16);                 AbstractBorder brdrRight = new TextBubbleBorder(Color.BLACK,2,16,16,false);                  JLabel l1 = new JLabel("Label 1");                 l1.setBorder(brdrRight);                 gui.add(l1);                  JLabel l2 = new JLabel("Label 2");                 l2.setBorder(brdrLeft);                 l2.setBackground(Color.YELLOW);                 l2.setOpaque(true);                 gui.add(l2);                  JPanel p1 = new JPanel();                 p1.add(new JLabel("Panel 1"));                 p1.setBorder(brdrRight);                 p1.setOpaque(false);                 gui.add(p1);                  JPanel p2 = new JPanel();                 p2.add(new JLabel("Panel 2"));                 p2.setBorder(brdrLeft);                 gui.add(p2);                  JOptionPane.showMessageDialog(null, gui);             }         };         // Swing GUIs should be created and updated on the EDT         // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html         SwingUtilities.invokeLater(r);     }  }  class TextBubbleBorder extends AbstractBorder {      private Color color;     private int thickness = 4;     private int radii = 8;     private int pointerSize = 7;     private Insets insets = null;     private BasicStroke stroke = null;     private int strokePad;     private int pointerPad = 4;     private boolean left = true;     RenderingHints hints;      TextBubbleBorder(             Color color) {         this(color, 4, 8, 7);     }      TextBubbleBorder(             Color color, int thickness, int radii, int pointerSize) {         this.thickness = thickness;         this.radii = radii;         this.pointerSize = pointerSize;         this.color = color;          stroke = new BasicStroke(thickness);         strokePad = thickness / 2;          hints = new RenderingHints(                 RenderingHints.KEY_ANTIALIASING,                 RenderingHints.VALUE_ANTIALIAS_ON);          int pad = radii + strokePad;         int bottomPad = pad + pointerSize + strokePad;         insets = new Insets(pad, pad, bottomPad, pad);     }      TextBubbleBorder(             Color color, int thickness, int radii, int pointerSize, boolean left) {         this(color, thickness, radii, pointerSize);         this.left = left;     }      @Override     public Insets getBorderInsets(Component c) {         return insets;     }      @Override     public Insets getBorderInsets(Component c, Insets insets) {         return getBorderInsets(c);     }      @Override     public void paintBorder(             Component c,             Graphics g,             int x, int y,             int width, int height) {          Graphics2D g2 = (Graphics2D) g;          int bottomLineY = height - thickness - pointerSize;          RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(                 0 + strokePad,                 0 + strokePad,                 width - thickness,                 bottomLineY,                 radii,                 radii);          Polygon pointer = new Polygon();          if (left) {             // left point             pointer.addPoint(                     strokePad + radii + pointerPad,                     bottomLineY);             // right point             pointer.addPoint(                     strokePad + radii + pointerPad + pointerSize,                     bottomLineY);             // bottom point             pointer.addPoint(                     strokePad + radii + pointerPad + (pointerSize / 2),                     height - strokePad);         } else {             // left point             pointer.addPoint(                     width - (strokePad + radii + pointerPad),                     bottomLineY);             // right point             pointer.addPoint(                     width - (strokePad + radii + pointerPad + pointerSize),                     bottomLineY);             // bottom point             pointer.addPoint(                     width - (strokePad + radii + pointerPad + (pointerSize / 2)),                     height - strokePad);         }          Area area = new Area(bubble);         area.add(new Area(pointer));          g2.setRenderingHints(hints);          // Paint the BG color of the parent, everywhere outside the clip         // of the text bubble.         Component parent  = c.getParent();         if (parent!=null) {             Color bg = parent.getBackground();             Rectangle rect = new Rectangle(0,0,width, height);             Area borderRegion = new Area(rect);             borderRegion.subtract(area);             g2.setClip(borderRegion);             g2.setColor(bg);             g2.fillRect(0, 0, width, height);             g2.setClip(null);         }          g2.setColor(color);         g2.setStroke(stroke);         g2.draw(area);     } } 
like image 182
Andrew Thompson Avatar answered Sep 20 '22 06:09

Andrew Thompson


Try this:

  JPanel p = new JPanel() {      @Override      protected void paintComponent(Graphics g) {         super.paintComponent(g);         Dimension arcs = new Dimension(15,15); //Border corners arcs {width,height}, change this to whatever you want         int width = getWidth();         int height = getHeight();         Graphics2D graphics = (Graphics2D) g;         graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);           //Draws the rounded panel with borders.         graphics.setColor(getBackground());         graphics.fillRoundRect(0, 0, width-1, height-1, arcs.width, arcs.height);//paint background         graphics.setColor(getForeground());         graphics.drawRoundRect(0, 0, width-1, height-1, arcs.width, arcs.height);//paint border      }   }; 

With my test:

  JFrame f = new JFrame();   f.setLayout(null);   f.setDefaultCloseOperation(3);   f.setSize(500, 500);   JPanel p = new JPanel() {      @Override      protected void paintComponent(Graphics g) {         super.paintComponent(g);         Dimension arcs = new Dimension(15,15);         int width = getWidth();         int height = getHeight();         Graphics2D graphics = (Graphics2D) g;         graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);           //Draws the rounded opaque panel with borders.         graphics.setColor(getBackground());         graphics.fillRoundRect(0, 0, width-1, height-1, arcs.width, arcs.height);//paint background         graphics.setColor(getForeground());         graphics.drawRoundRect(0, 0, width-1, height-1, arcs.width, arcs.height);//paint border      }   };   p.setBounds(10,10,100,30);   p.setOpaque(false);   f.getContentPane().setBackground(Color.red);   f.add(p);   f.show(); 

the result is:

Code result

like image 39
BackSlash Avatar answered Sep 22 '22 06:09

BackSlash