Java - 向JTabbedPane添加选项卡导致StackOverflowError(禁用触发ChangeEvent?)

时间:2012-06-21 00:17:15

标签: java swing stack-overflow jtabbedpane

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);
        }
    }
}
//**************************************************************************
}

我试过了:

  • 在addTab()方法中设置还原索引,因此选择了最新的选项卡(仍导致StackOverflowError)

请注意以下这一行:

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)
.
.
.

你有什么建议吗?我知道这有点模糊,但我真的不确定会出现什么问题。

有什么建议吗? 谢谢。 泰勒

3 个答案:

答案 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,这将触发选项卡的插入等...

所以你有两个简单的解决方案:

  1. 在添加新标签页之前使用设置为boolean的标记(true),并在完成后重新设置为false。在您测试是否需要添加标签的情况下,还要检查此标志是否为true
  2. 您在插入标签之前从JTabbedPane中删除了更改侦听器,然后将其放回。
  3. 在这两种情况下,使用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);
    }
}

我对代码进行了一些更改,使活动选项卡成为新创建的选项卡,并使索引稍微关闭。

希望这会有所帮助:)