在 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
事件。由于此事件,默认页面被删除(因此,选项卡的数量发生变化)。因此,addTab
中 index
的值不再正确并导致 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;
}
}
概览:
我必须开发一个 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
事件。由于此事件,默认页面被删除(因此,选项卡的数量发生变化)。因此,addTab
中 index
的值不再正确并导致 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;
}
}