paintComponent()正在绘制其他组件

我正在使用基于此答案中的代码的自定义类来绘制形状像讲话泡泡的背景。 每当我调整应用程序窗口的大小足以使组件在顶部或底部突出时,所述组件的轮廓将被绘制在JScrollPane之外的其他组件之上; 在这种情况下是JPanel

在左侧图像中,由于组件仍然可见,因此绘制了JScrollPane底部组件的边框; 而在右侧图像中,所提到的组件不再可见,一切看起来都是预期的。

我认为这与我使用JScrollPane来包含组件并因此允许组件在JPanel下滑动这一事实有关。 我该如何防止这种情况?

图片

主要:

 public class Main { public static void main(String[] args) { JPanel panel = new JPanel(), panelbar = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panelbar.setLayout(new FlowLayout()); JScrollPane scroll = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); JFrame frame = new JFrame(""); frame.setLayout(new BorderLayout()); frame.setSize(200, 223); for (int i = 0; i < 6; i++) { JLabel label = new JLabel("JLabel"); label.setBorder(new CustomBorder()); label.setOpaque(true); label.setBackground(Color.ORANGE); panel.add(label); } panelbar.add(new JLabel("JPanel")); frame.add(scroll, BorderLayout.CENTER); frame.add(panelbar, BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } } 

自定义类:

 public class CustomBorder extends AbstractBorder { private static final long serialVersionUID = 1L; Insets i; CustomBorder() { i = new Insets(10, 20, 10, 20); } @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { super.paintBorder(c, g, x, y, width, height); Polygon bubble = new Polygon(); bubble.addPoint(x + 10, y + 5); bubble.addPoint(x + width - 10, y + 5); bubble.addPoint(x + width - 10, y + height / 3); bubble.addPoint(x + width, y + height / 2); bubble.addPoint(x + width - 10, y + height * 2 / 3); bubble.addPoint(x + width - 10, y - 5 + height); bubble.addPoint(x + 10, y - 5 + height); Graphics2D g2d = (Graphics2D) g; Area rect = new Area(new Rectangle(x, y, width, height)); rect.subtract(new Area(bubble)); g2d.setClip(rect); g2d.setColor(c.getParent().getBackground()); g2d.fillRect(0, 0, width, height); g2d.setClip(null); g2d.setColor(Color.BLACK); g2d.draw(bubble); } @Override public Insets getBorderInsets(Component c) { return i; } @Override public Insets getBorderInsets(Component c, Insets insets) { return i; } } 

剪辑代码有两个问题:

  1. 在减去气泡时不会从原始剪辑开始(导致组件在滚动窗格外部绘制)
  2. 在绘制气泡之前,您不会恢复原始剪辑:

变化将是:

 @Override public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { super.paintBorder(c, g, x, y, width, height); Polygon bubble = new Polygon(); bubble.addPoint(x + 10, y + 5); bubble.addPoint(x + width - 10, y + 5); bubble.addPoint(x + width - 10, y + height / 3); bubble.addPoint(x + width, y + height / 2); bubble.addPoint(x + width - 10, y + height * 2 / 3); bubble.addPoint(x + width - 10, y - 5 + height); bubble.addPoint(x + 10, y - 5 + height); Graphics2D g2d = (Graphics2D) g; //Area rect = new Area(new Rectangle(x, y, width, height)); Shape clip = g2d.getClip(); Area rect = new Area(clip); rect.subtract(new Area(bubble)); g2d.setClip(rect); g2d.setColor(c.getParent().getBackground()); g2d.fillRect(0, 0, width, height); //g2d.setClip(null); g2d.setClip(clip); g2d.setColor(Color.BLACK); g2d.draw(bubble); } 

你的基本问题是,你正在改变在绘制组件之前设置的剪切区域,以及其他东西,这样可以让你超出组件的边界…

正如这里和这里所讨论的那样,边界不是要填充的,它们也不会影响paintComponent填充的区域

如果你看一下Paint paintBorder A Closer Look,你会看到在paintBorder之前调用paintComponent

javax.swing.JComponent扩展了这个类,并进一步将paint方法分解为三个单独的方法,这些方法按以下顺序调用:

  • protected void paintComponent(Graphics g)
  • protected void paintBorder(Graphics g)
  • protected void paintChildren(Graphics g)

那么,解决方案是什么? 假装!

假装

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Polygon; import javax.swing.BoxLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.border.EmptyBorder; public class BorderCheat { public static void main(String[] args) { new BorderCheat(); } public BorderCheat() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JPanel panel = new JPanel(), panelbar = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panelbar.setLayout(new FlowLayout()); JScrollPane scroll = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); for (int i = 0; i < 6; i++) { BubblePane bp = new BubblePane(); bp.setBackground(Color.ORANGE); JLabel label = new JLabel("JLabel"); bp.add(label); panel.add(bp); } panelbar.add(new JLabel("JPanel")); JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(scroll); frame.add(panelbar, BorderLayout.SOUTH); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class BubblePane extends JPanel { public BubblePane() { setLayout(new GridBagLayout()); setBorder(new EmptyBorder(10, 20, 10, 30)); setOpaque(false); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); Insets insets = getInsets(); int x = 0; int y = 0; int width = getWidth(); int height = getHeight(); Polygon bubble = new Polygon(); bubble.addPoint(x, y); bubble.addPoint(x + width - insets.right + 10, y); bubble.addPoint(x + width - insets.right + 10, y + height / 3); bubble.addPoint(x + width, y + height / 2); bubble.addPoint(x + width - insets.right + 10, y + height * 2 / 3); bubble.addPoint(x + width - insets.right + 10, y + height); bubble.addPoint(x, y + height); g2d.setColor(getBackground()); g2d.fill(bubble); g2d.setColor(Color.BLACK); g2d.draw(bubble); g2d.dispose(); } } } 

好吧,“但他们之间没有差距”,你说。 好的,所以使用CompoundBorder或布局,允许您指定组件之间的垂直或水平间距......