Hey StackOverflow社区,我遇到了StackOverflow问题。
当选择[+]选项卡时,我在向GUI的JTabbedPane容器添加新选项卡时遇到困难。到目前为止,每当我单击[+]选项卡时,都会附加新选项卡,直到发生StackOverflowError。
如果满足以下条件,则会在JTabbedPane中添加一个新选项卡。
if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1){
...
}
我试图恢复到之前选择的标签,以避免将标签重复添加到JTabbedPane,但无济于事。当ChangeEvent执行器被触发时,它会无限期地保持不变吗?我在SE7 API中没有遇到任何有用的东西。
相关代码(不可编辑,摘录较大的程序。可能会丢失括号,只是因为我复制粘贴了代码的摘录,并且容易出错)
@Override
public void init(){
setLayout(new GridLayout(MAIN_LAYOUT_ROWS, MAIN_LAYOUT_COLUMNS));
add(renderPanel = new JScrollPane());
add(controlPanel = new JPanel());
add(colourPanel = new JPanel());
add(songPanel = new JTabbedPane());
//songPanel options
songPanel = new JTabbedPane();
songPanel.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
songPanel.addTab("#1", new JTextArea());
songPanel.addTab("+", null, new JLabel(), "+");
Container cp = getContentPane();
cp.add(BorderLayout.SOUTH, songPanel);
//integrate songPanel changeListener
songPanel.addChangeListener(new ChangeListener(){
@Override //Method called when selected tab changes
public void stateChanged(ChangeEvent e){
try {
if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1){
addTab("songPanel");
}
} catch (StackOverflowError soe){soe.printStackTrace();}
}
});
//*************************************************************************
@Override
public void start(){
}
//*************************************************************************
private void addTab(String panelName){
System.out.println("ADDING TAB");
if(panelName.equals("songPanel")){
String tabName = ("#" + Integer.toString(songPanel.getTabCount()-1));
songPanel.insertTab(tabName, null, new JTextField(), tabName, songPanel.getTabCount()-2);
}
}
}
//**************************************************************************
}
我试过了:
请注意以下这一行:
songPanel.getSelectedIndex()==songPanel.getTabCount()-1)
“songPanel.getSelectedIndex()”和“songPanel.getTabCount() - 1)”始终相等,因此条件始终为真(导致StackOverflowError )
错误讯息:
java.lang.StackOverflowError
at javax.swing.text.StyleContext$SmallAttributeSet.getAttributeNames(StyleContext.java:947)
at javax.swing.text.StyleContext$SmallAttributeSet.containsAttributes(StyleContext.java:973)
at javax.swing.text.StyleContext$SmallAttributeSet.equals(StyleContext.java:852)
at java.util.WeakHashMap.eq(WeakHashMap.java:282)
at java.util.WeakHashMap.get(WeakHashMap.java:379)
at java.util.Collections$SynchronizedMap.get(Collections.java:2031)
at javax.swing.text.StyleContext.getImmutableUniqueSet(StyleContext.java:520)
at javax.swing.text.StyleContext.addAttributes(StyleContext.java:340)
at javax.swing.text.AbstractDocument$AbstractElement.addAttributes(AbstractDocument.java:1985)
at javax.swing.text.AbstractDocument$AbstractElement.<init>(AbstractDocument.java:1777)
at javax.swing.text.AbstractDocument$LeafElement.<init>(AbstractDocument.java:2502)
at javax.swing.text.AbstractDocument$BidiElement.<init>(AbstractDocument.java:2674)
at javax.swing.text.AbstractDocument.<init>(AbstractDocument.java:149)
at javax.swing.text.AbstractDocument.<init>(AbstractDocument.java:109)
at javax.swing.text.PlainDocument.<init>(PlainDocument.java:90)
at javax.swing.text.PlainDocument.<init>(PlainDocument.java:80)
at javax.swing.text.DefaultEditorKit.createDefaultDocument(DefaultEditorKit.java:130)
at javax.swing.plaf.basic.BasicTextUI.installUI(BasicTextUI.java:799)
at javax.swing.JComponent.setUI(JComponent.java:655)
at javax.swing.text.JTextComponent.setUI(JTextComponent.java:338)
at javax.swing.text.JTextComponent.updateUI(JTextComponent.java:348)
at javax.swing.text.JTextComponent.<init>(JTextComponent.java:322)
at javax.swing.JTextField.<init>(JTextField.java:231)
at javax.swing.JTextField.<init>(JTextField.java:172)
at application.Analyzer.addTab(Analyzer.java:133)
at application.Analyzer.access$100(Analyzer.java:24)
at application.Analyzer$1.stateChanged(Analyzer.java:101)
at javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:416)
at javax.swing.JTabbedPane$ModelListener.stateChanged(JTabbedPane.java:270)
at javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132)
at javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:67)
at javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616)
at javax.swing.JTabbedPane.insertTab(JTabbedPane.java:735)
at application.Analyzer.addTab(Analyzer.java:133)
at application.Analyzer.access$100(Analyzer.java:24)
.
.
.
你有什么建议吗?我知道这有点模糊,但我真的不确定会出现什么问题。
有什么建议吗? 谢谢。 泰勒
答案 0 :(得分:1)
StackOverflow标识无限递归。所以首先要找到递归。在您的情况下,这些是标识递归的堆栈跟踪的行:
在application.Analyzer.addTab(Analyzer.java:133)at application.Analyzer.access $ 100(Analyzer.java:24)at application.Analyzer $ 1.stateChanged(Analyzer.java:101)at javax.swing.JTabbedPane.fireStateChanged(JTabbedPane.java:416)at javax.swing.JTabbedPane中的$ ModelListener.stateChanged(JTabbedPane.java:270) 在 javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132) 在 javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultSingleSelectionModel.java:67) 在javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616) 在javax.swing.JTabbedPane.insertTab(JTabbedPane.java:735)at application.Analyzer.addTab(Analyzer.java:133)
因此,当您插入选项卡时,它会自动触发所选选项卡的更改,该选项卡又会调用您的ChangeEventListener,这将触发选项卡的插入等...
所以你有两个简单的解决方案:
boolean
的标记(true
),并在完成后重新设置为false
。在您测试是否需要添加标签的情况下,还要检查此标志是否为true
。在这两种情况下,使用try / finally块确保返回到一致状态。
答案 1 :(得分:0)
更新的解决方案 抱歉,以前的解决方案没有按预期工作。在这里我更新了一个:
public class TabbedPaneTest {
private final static JButton ADD_NEW_TAB_BUTTON = new JButton();
private JFrame mainFrame;
private JTabbedPane tabbedPane;
public void run() {
mainFrame = new JFrame("Test JTabbedPane");
mainFrame.setSize(300, 400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tabbedPane = new JTabbedPane();
tabbedPane.addTab("default new tab", new JLabel("this is a default tab"));
addNewTabButton();
tabbedPane.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if (tabbedPane.getSelectedComponent() == ADD_NEW_TAB_BUTTON) {
tabbedPane.addTab("new tab", new JLabel("new tab label"));
addNewTabButton();
}
}
});
mainFrame.getContentPane().add(tabbedPane);
mainFrame.setVisible(true);
}
private void addNewTabButton() {
tabbedPane.remove(ADD_NEW_TAB_BUTTON);
tabbedPane.addTab("[+]", ADD_NEW_TAB_BUTTON);
}
public static void main(String[] params) {
TabbedPaneTest test = new TabbedPaneTest();
test.run();
}
}
答案 2 :(得分:0)
问题是在使用+选项卡作为选定选项卡再次添加后调用changeListener,从而导致创建新选项卡,依此类推。
一个非常简单的解决方案可能只是添加一个bool标志,如Guillaume Polet所说:
songPanel.addChangeListener(new ChangeListener(){
@Override //Method called when selected tab changes
public void stateChanged(ChangeEvent e){
try {
if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1 && !adding){
adding = true;
addTab("songPanel");
adding = false;
}
} catch (StackOverflowError soe){soe.printStackTrace();}
}
});
添加标志是初始化为false的类字段,表示您是否正在添加选项卡。 对addTab进行微小更改以使一切正常工作:
private void addTab(String panelName){
System.out.println("ADDING TAB");
if(panelName.equals("songPanel")){
String tabName = ("#" + Integer.toString(songPanel.getTabCount()));
int index = songPanel.getTabCount()-1;
songPanel.insertTab(tabName, null, new JTextField(), tabName, index);
songPanel.setSelectedIndex(index);
}
}
我对代码进行了一些更改,使活动选项卡成为新创建的选项卡,并使索引稍微关闭。
希望这会有所帮助:)