Граница с закругленными углами и прозрачностью

На следующем скриншоте показан тест TextBubbleBorder 1 . Я хотел бы сделать углы компонента, которые находятся за пределами прямоугольника, полностью прозрачными и показать, какой компонент находится под ним. Я нашел способ ограничить цвет BG ярлыка «внутри границы», установив Clip (представляющий область за пределами закругленных углов) на экземпляре Graphics2D и вызвав clearRect() . Это можно увидеть в Label 1 .

Тест границ

Однако вы можете увидеть недостаток этого подхода, когда на родительской панели есть красный BG (или любой нестандартный цвет). Углы по умолчанию соответствуют цвету панели по умолчанию (проще всего увидеть на Panel 2 ).

В конечном итоге мне хотелось бы, чтобы это работало для нестандартного цвета в родительском контейнере, но частично это было вдохновлено тем, что мне нужно сделать для репликации этого компонента с помощью gradleиентной краски?

Кто-нибудь знает, как сделать эти углы прозрачными?

 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. Хотя TextBubbleBorder был разработан для внутреннего дополнения для JTextArea с фоновым изображением (& закончил с использованием JLabel так как текстовая область была беспорядок по причинам, упомянутым выше), указав pointerSize 0, мы получим «округленный прямоугольник» вместо ,

    NB В этом коде есть обрезающая ошибка, которая зафиксирована в принятом ответе paintComponent (), нарисованная на других компонентах . Это следует рассматривать только как решение, если включено «исправление ошибок отсечения».


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

    См. Этот пункт в коде для источника, который отображается правильно:

    BorderTest с указателем 0px

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

    Попробуй это:

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

    С моим тестом:

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

    результат:

    Результат кода

    Спасибо @BackSlash, приятно и просто. Я расширил это, чтобы сделать его более многоразовым. Это также позволяет установить цвет фона в конструкторе. И я показываю, как вы можете сделать круговую панель для удовольствия.

    введите описание изображения здесь

     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 } } } 
    Давайте будем гением компьютера.