[英]Java - Adding tab to JTabbedPane causing StackOverflowError (Disabling a firing ChangeEvent?)
嘿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)始終相等,因此條件始終為true( 導致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)
.
.
.
你有什么建議嗎? 我知道這有點含糊,但是我真的不確定到底出了什么問題。
有什么建議么? 謝謝。 泰勒
StackOverflow標識無限遞歸。 所以第一件事就是找到那個遞歸。 在您的情況下,這是用於標識該遞歸的stacktrace的各行:
在application.Analyzer.addTab(Analyzer.java:133)在application.Analyzer.access $ 100(Analyzer.java:24)在application.Analyzer $ 1.stateChanged(Analyzer.java:101)在javax.swing.JTabbedPane.fireStateChanged( JxabbedPane.java:416)在javax.swing.JTabbedPane $ ModelListener.stateChanged(JTabbedPane.java:270)在javax.swing.DefaultSingleSelectionModel.fireStateChanged(DefaultSingleSelectionModel.java:132)在javax.swing.DefaultSingleSelectionModel.setSelectedIndex(DefaultS) :67),位於javax.swing.JTabbedPane.insertTab(JTabbedPane.java:735)處的javax.swing.JTabbedPane.setSelectedIndexImpl(JTabbedPane.java:616)在application.Analyzer.addTab(Analyzer.java:133)
因此,當您插入選項卡時,它會自動觸發所選選項卡的更改,這又會調用ChangeEventListener,這將觸發選項卡的插入等。
因此,您有兩個簡單的解決方案:
true
的標志( boolean
),並在完成后將其設置為false
。 在測試是否需要添加選項卡的條件下,還要檢查此標志是否為true
。 在這兩種情況下,請使用try / finally塊以確保返回一致狀態。
更新的解決方案很抱歉,以前的解決方案無法正常工作。 這里是我更新的:
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();
}
}
問題在於,添加后再次調用changeListener,並使用+選項卡作為選定選項卡,從而導致創建新選項卡,依此類推。
一個非常簡單的解決方案可能就是像Guillaume Polet所說的那樣添加一個bool標志:
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);
}
}
我對代碼進行了一些更改,使活動選項卡成為新創建的選項卡,並使索引有些偏離。
希望這可以幫助 :)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.