Borda com cantos arredondados e transparência

A captura de canvas a seguir mostra um teste de TextBubbleBorder 1 . Eu gostaria de fazer os cantos do componente que estão fora do retângulo para ser totalmente transparente e mostrar o componente que está abaixo dele. Eu encontrei uma maneira de restringir a cor BG de um label para “dentro da borda”, definindo um Clip (representando a área fora dos cantos arredondados) na instância Graphics2D e chamando clearRect() . Isso pode ser visto no Label 1 .

Teste de fronteira

No entanto, você pode ver a desvantagem dessa abordagem quando há um BG vermelho (ou qualquer cor não padrão) no painel pai. Os cantos são padronizados para a cor do painel padrão (mais fácil de ver no Panel 2 ).

Por fim, gostaria que isso funcionasse para uma cor não padrão no contêiner pai, mas foi parcialmente inspirado em O que preciso fazer para replicar esse componente com a pintura de gradiente?

Alguém sabe uma maneira de obter esses cantos 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. Enquanto o TextBubbleBorder foi criado para o preenchimento interno para JTextArea com a imagem de fundo (& acabou usando um JLabel desde que a área de texto foi uma bagunça pelas razões mencionadas acima), especificando um pointerSize de 0 acabamos com um ‘retângulo arredondado’ .

NB Há um erro de recorte neste código, que é corrigido na resposta aceita para paintComponent () está desenhando em outros componentes . Isso só deve ser considerado como uma solução se a ‘correção do erro de recorte’ for incorporada.


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

Veja este ponto no código da fonte que mostra corretamente como:

BorderTest com ponteiro de fala 0px

BorderTest com ponteiro de fala de 16px

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

Tente isto:

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

Com meu teste:

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

o resultado é:

Resultado de código

Obrigado @BackSlash, legal e simples. Eu expandi isso, então é mais reutilizável. Isso também permite definir uma cor de fundo no construtor. E eu mostro como você pode fazer um painel circular por diversão.

insira a descrição da imagem aqui

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