I'm using a custom class, based on the code in this answer, to draw a background shaped like a speech bubble. Whenever I resize the window of my application enough to make a component poke out at the top or bottom, the outlines of the said component is drawn outside the JScrollPane
on top of other components; in this case the JPanel
.
In the left-side image, the border of the component at the bottom of the JScrollPane
is drawn, due to the component still being visible; while in the right-side image, the mentioned component is no longer visible and everything looks as intended.
I believe it has something to do with the fact that I'm using a JScrollPane
to contain the components and thus allowing the component to slide under the JPanel
. How do I prevent this?
Main:
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);
}
}
Custom class:
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;
}
}
Your basic problem is, you're changing the clipping area, which was set before the component was painted, to something, well, else, which is allowing you to paint beyond the bounds of the component...
As discussed here and here, borders aren't meant to be filled, nor do they effect the area filled by
paintComponent
If you take a look at A Closer Look at the Paint Mechanism you will see the
paintComponent
is called beforepaintBorder
...So, what's the solution? Fake it!
Okay, "but there's no gap between them" you say. Okay, so use a
CompoundBorder
or a layout which allows you to specify the vertical or horizontal spacing between components...There are two problems with the clipping code:
The changes would be: