在 JTabbedPane 中设置自定义 header 组件 (TabComponent) 的正确方法

Correct way to set custom header component (TabComponent) in JTabbedPane

概览:

我必须开发一个 JTabbedPane,它应该有一个默认选项卡,只有在没有显示其他选项卡时才会显示(我使用 ContainerListener 实现)。

此外,它应该为每个添加的选项卡创建一个自定义 header(我实现了覆盖 addTab)。

下面一个MCVE可以用as-is:

public class MyCustomTabbedPane extends JTabbedPane {

    private final JPanel defaultTab = new JPanel();

    public MyCustomTabbedPane() {
        super();

        JButton exampleButton = new JButton("Click me");
        exampleButton.addActionListener((e) -> addTab("New page", null, new JPanel()));
        defaultTab.add(exampleButton);

        addTab("Default", null, defaultTab);
        addContainerListener(new ContainerListener() {

            @Override
            public void componentRemoved(ContainerEvent e) {
                if (getTabCount() == 0) {
                    addTab("Default", null, defaultTab);
                }
            }

            @Override
            public void componentAdded(ContainerEvent e) {
                if (getTabCount() > 1) {
                    remove(defaultTab);
                }
            }
        });
    }

    @Override
    public void addTab(String title, Icon icon, Component component) {
        int index = getTabCount();
        // Add new tab at the back with default header
        super.addTab(title, icon, component);
        // Create custom header based on default header
        Component header = createHeader(title, icon, getTabComponentAt(index));
        // Set custom header for added tab
        setTabComponentAt(index, header);
        // Select the added tab
        setSelectedIndex(index);
    }

    private Component createHeader(String title, Icon icon, Component header) {
        if(header == null) {
            header = new JLabel(title, icon, SwingConstants.LEFT);
        }
        JPanel panel = new JPanel();
        panel.add(new JLabel("Just an example header modification - "));
        panel.add(header);
        return panel;
    }
}

问题:

问题发生在 addTab 覆盖的 super.addTab(title, icon, component); 处,因为它在此时触发了 componentAdded 事件。由于此事件,默认页面被删除(因此,选项卡的数量发生变化)。因此,addTabindex 的值不再正确并导致 java.lang.IndexOutOfBoundsException: Index: 1, Size: 1.

问题:

我可以告诉 JTabbedPane 延迟 super.addTab(title, icon, component)setTabComponentAt(index, header) 之间的事件吗?如果这不可能,是否有另一种方法如何添加带有自定义 header 的新选项卡,而不首先添加带有默认 header 的选项卡?

好吧,我想最简单的方法是在 super.addTab() 行之后添加一行 doing
index = index == getTabCount() ? 0 : index
index = index == getTabCount() ? index - 1 : index
if (index == getTabCount()) { index-- }

我能够通过使用以下代码摆脱 addTab() 中对 index 的需要(参见 setSelectedComponent() 替换 setSelectedIndex()indexOfComponent(component) 替换 index 上的用途)。 这样,我就避免了对无效索引的访问。

public class MyCustomTabbedPane extends JTabbedPane {

    private final JPanel defaultTab = new JPanel();

    public MyCustomTabbedPane() {
        super();

        JButton exampleButton = new JButton("Click me");
        exampleButton.addActionListener((e) -> addTab("New page", null, new JPanel()));
        defaultTab.add(exampleButton);

        addTab("Default", null, defaultTab);
        addContainerListener(new ContainerListener() {

            @Override
            public void componentRemoved(ContainerEvent e) {
                if (getTabCount() == 0) {
                    addTab("Default", null, defaultTab);
                }
            }

            @Override
            public void componentAdded(ContainerEvent e) {
                if (getTabCount() > 1) {
                    remove(defaultTab);
                }
            }
        });
    }

    @Override
    public void addTab(String title, Icon icon, Component component) {
        // Add new tab at the back with default header
        super.addTab(title, icon, component);
        // Create custom header based on default header
        Component header = createHeader(title, icon, getTabComponentAt(indexOfComponent(component)));
        // Set header for previously added tab
        setTabComponentAt(indexOfComponent(component), header);
        // Select the added tab
        setSelectedComponent(component);
    }

    private Component createHeader(String title, Icon icon, Component header) {
        if(header == null) {
            header = new JLabel(title, icon, SwingConstants.LEFT);
        }
        JPanel panel = new JPanel();
        panel.add(new JLabel("Just an example header modification - "));
        panel.add(header);
        return panel;
    }
}