简体   繁体   English

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

[英]Java - Adding tab to JTabbedPane causing StackOverflowError (Disabling a firing ChangeEvent?)

Hey StackOverflow community, I'm having a StackOverflow problem. 嘿StackOverflow社区,我遇到了StackOverflow问题。

I'm having difficulty adding a new tab to my GUI's JTabbedPane container, when the [+] tab is selected. 当选择[+]选项卡时,我很难在GUI的JTabbedPane容器中添加新选项卡。 So far, whenever I click the [+] tab, new tabs are appended until a StackOverflowError occurs. 到目前为止,每当我单击[+]选项卡时,都会附加新选项卡,直到发生StackOverflowError。

A new tab is added to the JTabbedPane when the following condition is true. 当满足以下条件时,将向JTabbedPane添加新选项卡。

if(songPanel.getSelectedIndex()==songPanel.getTabCount()-1){
...
}

I've tried to revert back to the previously selected tab to avoid tabs being added repeatedly to the JTabbedPane, to no avail. 我试图恢复到先前选择的选项卡,以避免将选项卡重复添加到JTabbedPane,但无济于事。 When the ChangeEvent actuator is fired, does it stay on indefinitely? 触发ChangeEvent执行器后,它会无限期保持打开状态吗? I haven't come across anything useful in the SE7 API. 我没有在SE7 API中遇到任何有用的东西。

Relevant code (non-compilable, excerpt from larger program. May be missing brackets, only because I copy-pasted excerpts of the code, and liable to make mistakes) 相关代码(不可编译,摘自较大的程序。可能会缺少方括号,仅是因为我复制粘贴了该代码的摘录,并容易出错)

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

I've tried: 我试过了:

  • Setting a revert index in the addTab() method, so the newest tab is selected (still results in StackOverflowError) 在addTab()方法中设置一个还原索引,以便选择最新的选项卡(仍会导致StackOverflowError)

Note this line: 注意这一行:

songPanel.getSelectedIndex()==songPanel.getTabCount()-1)

Both "songPanel.getSelectedIndex()" and "songPanel.getTabCount()-1)" are always equal, so condition is always true ( causing the StackOverflowError ) “ songPanel.getSelectedIndex()”和“ songPanel.getTabCount()-1)始终相等,因此条件始终为true( 导致StackOverflowError

Error message: 错误信息:

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

Do you have any suggestions? 你有什么建议吗? I know it's kind of vague, but I'm really not sure what is going wrong. 我知道这有点含糊,但是我真的不确定到底出了什么问题。

Any suggestions? 有什么建议么? Thanks. 谢谢。 Tyler 泰勒

StackOverflow identifies an infinite recursion. StackOverflow标识无限递归。 So first thing is to find that recursion. 所以第一件事就是找到那个递归。 In your case these are the lines of the stacktrace that identifies that recursion: 在您的情况下,这是用于标识该递归的stacktrace的各行:

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

So when you insert a tab, it automatically triggers a change of selected tab which in turns calls your ChangeEventListener which will trigger the insertion of a tab etc... 因此,当您插入选项卡时,它会自动触发所选选项卡的更改,这又会调用ChangeEventListener,这将触发选项卡的插入等。

So you have two simple solutions: 因此,您有两个简单的解决方案:

  1. Use a flag (a boolean ) that is set to true before you add the new tab and that you set back to false when you are done. 在添加新选项卡之前,请使用设置为true的标志( boolean ),并在完成后将其设置为false In your condition to test if you need to add a tab, you also check that this flag is not true . 在测试是否需要添加选项卡的条件下,还要检查此标志是否为true
  2. You remove your change listener from the JTabbedPane before you insert the tab and you put it back afterwards. 在插入选项卡之前,您需要从JTabbedPane中删除更改侦听器,然后再将其放回去。

In both case, use a try/finally block to make sure to return to a consistent state. 在这两种情况下,请使用try / finally块以确保返回一致状态。

UPDATED SOLUTION Sorry, the previous solution is not working as expected. 更新的解决方案很抱歉,以前的解决方案无法正常工作。 Here my updated one: 这里是我更新的:

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();
    }

}

The problem is that the changeListener is called after the adding once again with the + tab as the selected tab, causing the creation of a new tab, and so on. 问题在于,添加后再次调用changeListener,并使用+选项卡作为选定选项卡,从而导致创建新选项卡,依此类推。

a pretty simple solution could be just to add a bool flag as Guillaume Polet stated : 一个非常简单的解决方案可能就是像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();}
        }
    });

The adding flag is a class field initialized to false, and indicates whether or not you are in progress of adding a tab. 添加标志是一个初始化为false的类字段,它指示您是否正在添加选项卡。 Minor changes to the addTab to get everything to work: 对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);
    }
}

I made a little change in code, making the active tab the newly created one, and the indexes where a bit off. 我对代码进行了一些更改,使活动选项卡成为新创建的选项卡,并使索引有些偏离。

Hope this helps :) 希望这可以帮助 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM