AbstractBorder 在 JTabbedPane 上绘制

AbstractBorder paints over JTabbedPane

所以我正在使用 Swing 并尝试使用 JTabbedPane 来包含多个 JPanel 和 JScrollPanes 以滚动浏览 JPanel 列表。出现的问题是每当我更改选项卡时,除其他外,我在滚动窗格内的 JPanels 中拥有的自定义边框功能导致组件在 JTabbedPane header 顶部绘制,我不知道如何要解决此问题:AbstractBorder paints over JTabbedPane header。我提供了模仿我在下面尝试做的事情的代码。要重现该问题,请在一个选项卡上向下滚动一些,直到轮廓面板之一在顶部半可见。然后,切换到另一个选项卡,然后再返回。然后,再滚动一些,问题将出现在 JTabbedPane

的顶部
public class Example extends JFrame {
private final JTabbedPane tabbedPane;
public Example() {
    setTitle("MIN Example");
    setSize(600, 700);
    setLayout(new BorderLayout());
    setLocationRelativeTo(null);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBackground(Color.BLACK);
    setExtendedState(JFrame.MAXIMIZED_BOTH);

    tabbedPane = new JTabbedPane();
    tabbedPane.setBorder(BorderFactory.createEmptyBorder());
    tabbedPane.setFocusable(false);

    add(tabbedPane, BorderLayout.CENTER);
    createTabs();

    setVisible(true);
}

private void createTabs() {
    // Get the list of guilds
    List<String> tabs = new ArrayList<>(Arrays.asList("Tab 1", "Tab 2", "Tab 3"));

    // Iterate through the tabs and create the tab
    for (String tab : tabs) {
        // Initialize the list JPanel and formatting
        JPanel panelList = new JPanel();
        panelList.setLayout(new GridBagLayout());

        // House the list panel inside a JScrollPane
        JScrollPane listScroll = new JScrollPane(panelList);
        listScroll.setBorder(BorderFactory.createEmptyBorder());

        // Create tab and formatting
        tabbedPane.addTab(tab, listScroll);
        populateList(panelList);
    }
}

private void populateList(JPanel tab) {
    // Create GBC for formatting
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbc.weightx = 1;
    gbc.weighty = 1;

    // Remove all components from the JPanel
    tab.removeAll();

    // Add filler JPanel
    JPanel filler = new JPanel();
    filler.setOpaque(false);
    tab.add(filler, gbc);

    // Update GBC constraints
    gbc.insets = new Insets(10, 10, 0, 10);
    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbc.weighty = GridBagConstraints.RELATIVE;

    // Go through the tabs and add JPanels
    for (int i = 10; i >= 0; i--) {
        JPanel tempPanel = new JPanel();
        tempPanel.setBorder(new RoundedBorder(Color.BLACK, 6, 16));
        tab.add(tempPanel, gbc, 0);
        tempPanel.setPreferredSize(new Dimension(0, 100));
    }

    // Refresh the console to display updated lists
    validate();
    repaint();
}
}

public class RoundedBorder extends AbstractBorder {
private final Color color;
private final int thickness;
private final int radii;
private final Insets insets;
private final BasicStroke stroke;
private final int strokePad;
RenderingHints hints;

/**
 * Creates the rounded border
 *
 * @param color The color of the border outline
 * @param thickness The thickness of the border outline
 * @param radii The radius of the rounded border
 */
public RoundedBorder(Color color, int thickness, int radii) {
    this.thickness = thickness;
    this.radii = radii;
    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 + 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;

    RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(strokePad, strokePad, width - thickness, bottomLineY, radii, radii);

    Area area = new Area(bubble);

    g2.setRenderingHints(hints);

    // Paint the background color of the parent
    Component parent  = c.getParent();
    if (parent != null) {
        Color background = parent.getBackground();
        Rectangle rect = new Rectangle(0,0,width, height);
        Area borderRegion = new Area(rect);
        borderRegion.subtract(area);
        g2.setClip(borderRegion);
        g2.setColor(background);
        g2.fillRect(0, 0, width, height);
        g2.setClip(null);
    }

    g2.setColor(color);
    g2.setStroke(stroke);
    g2.draw(area);
}
}

我试图解决此问题的唯一方法是更改​​添加 JTabbedPane 的顺序(在选项卡填充面板之前添加)但无济于事。将不胜感激,谢谢。

将来,MRE 应该包括:

  1. 导入语句
  2. main() 方法

在原始代码中,Graphics 的“clip”被设置为 null,因此面板的整个 Rectangle 看起来都被绘制了。

我修改了父级的绘画以使用单独的 Graphics 对象,因此原始“剪辑”区域不受影响:

// Paint the background color of the parent
Component parent  = c.getParent();
if (parent != null) {
    Graphics2D g2d = (Graphics2D)g2.create();
    Color background = parent.getBackground();
    //Rectangle rect = new Rectangle(0, 0, width, height);
    Rectangle rect = g2d.getClip().getBounds();
    Area borderRegion = new Area(rect);
    borderRegion.subtract(area);
    g2d.setClip(borderRegion);
    g2d.setColor(background);
    g2d.fillRect(0, 0, width, height);
    g2d.dispose();
}