Borde con esquinas redondeadas y transparencia

La siguiente captura de pantalla muestra una prueba de TextBubbleBorder 1 . Me gustaría hacer que las esquinas del componente que están fuera del rectángulo sean completamente transparentes y mostrar cualquier componente que esté debajo de él. Encontré una forma de restringir el color BG de una etiqueta a ‘dentro del borde’ configurando un Clip (que representa el área fuera de las esquinas redondeadas) en la instancia de Graphics2D y llamando a clearRect() . Eso se puede ver en la Label 1 .

Prueba fronteriza

Sin embargo, puede ver la desventaja de este enfoque cuando hay un BG rojo (o cualquier color no estándar) en el panel principal. Las esquinas tienen por defecto el color predeterminado del panel (lo más fácil de ver en el Panel 2 ).

En última instancia, me gustaría que esto funcione para un color no estándar en el contenedor principal, pero en parte fue inspirado por ¿Qué debo hacer para reproducir este componente con pintura de degradado?

¿Alguien sabe una forma de obtener esas esquinas transparentes?

 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. Mientras que TextBubbleBorder se diseñó para el relleno interno para JTextArea con Imagen de fondo (y terminó usando JLabel ya que el área de texto era un desastre por los motivos mencionados anteriormente), al especificar un pointerSize de 0 terminamos con un ‘rectángulo redondeado’ .

NB Hay un error de recorte en este código, que se corrige en la respuesta aceptada para paintComponent () se basa en otros componentes . Esto solo debe considerarse como una solución si se incorpora el ‘arreglo de errores de recorte’.


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

Vea este punto en el código de la fuente que se muestra correctamente como:

BorderTest con 0px puntero de voz

BorderTest con puntero de voz de 16 px

 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); } } 

Prueba esto:

  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 } }; 

Con mi prueba:

  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(); 

el resultado es:

Código de resultado

Gracias @BackSlash, agradable y simple. Expandí esto así que es más reutilizable. Esto también permite establecer un color de fondo en el constructor. Y te muestro cómo puedes hacer un panel circular para divertirte.

enter image description here

 import java.awt.*; import javax.swing.*; public class RoundedPanelExample extends JFrame { public RoundedPanelExample() { setDefaultCloseOperation(EXIT_ON_CLOSE); setTitle("Rounded Panel Example"); setResizable(true); setDefaultLookAndFeelDecorated(true); setSize(500, 500); Container pane = getContentPane(); pane.setLayout(null); pane.setBackground(Color.LIGHT_GRAY); JPanel p1 = new RoundedPanel(10, Color.CYAN); p1.setBounds(10,10,100,60); p1.setOpaque(false); pane.add(p1); JPanel p2 = new RoundedPanel(15, Color.RED); p2.setBounds(150,10,50,50); p2.setOpaque(false); pane.add(p2); JPanel p3 = new RoundedPanel(30); p3.setBounds(230,10,100,150); p3.setOpaque(false); pane.add(p3); JPanel p4 = new RoundedPanel(20); p4.setBounds(10,200,100,100); p4.setBackground(Color.GREEN); p4.setOpaque(false); pane.add(p4); JPanel p5 = new RoundedPanel(200); p5.setBounds(150,200,200,200); p5.setBackground(Color.BLUE); p5.setOpaque(false); pane.add(p5); } public static void main(String[] args) { RoundedPanelExample gui = new RoundedPanelExample(); gui.setVisible(true); } class RoundedPanel extends JPanel { private Color backgroundColor; private int cornerRadius = 15; public RoundedPanel(LayoutManager layout, int radius) { super(layout); cornerRadius = radius; } public RoundedPanel(LayoutManager layout, int radius, Color bgColor) { super(layout); cornerRadius = radius; backgroundColor = bgColor; } public RoundedPanel(int radius) { super(); cornerRadius = radius; } public RoundedPanel(int radius, Color bgColor) { super(); cornerRadius = radius; backgroundColor = bgColor; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Dimension arcs = new Dimension(cornerRadius, cornerRadius); 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. if (backgroundColor != null) { graphics.setColor(backgroundColor); } else { 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 } } }