Java 调用JPanel子类上的RemoveAll()时出现奇怪的Swing错误

Java 调用JPanel子类上的RemoveAll()时出现奇怪的Swing错误,java,swing,Java,Swing,我正在开发一个数独益智生成器,在调用JPanel的RemoveAll()方法之后/期间遇到了一些间歇性的swing异常。在eclipse的调试模式下运行时,不会出现异常。以下是相关课程的代码: import java.awt.FlowLayout; import java.awt.GridLayout; import javax.swing.JLabel; import javax.swing.JPanel; /** * Represents a cell in the GUI Grid

我正在开发一个数独益智生成器,在调用JPanel的RemoveAll()方法之后/期间遇到了一些间歇性的swing异常。在eclipse的调试模式下运行时,不会出现异常。以下是相关课程的代码:

import java.awt.FlowLayout;
import java.awt.GridLayout;

import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * Represents a cell in the GUI Grid display
 * @author alex
 *
 */
public class CellGUI extends JPanel {



    public CellGUI()
    {
        super();

        this.setLayout(new GridLayout(3,3));

        for(int i = 1;i <=9;i++)
        {
            add(new JLabel("" + i));
        }

        setVisible(true);
    }

    public void clear()
    {
        this.removeAll();
        this.validate();
        this.setLayout(new GridLayout(3,3));

        for(int i = 1;i <= 9; i++)
        {
            add(new JLabel("" + i));
        }       

    }

    public void setValue(int newVal)
    {
        if (newVal == 0)
        {
            clear();
        }
        else
        {

            this.removeAll(); // this line appears to be the problem 

            //this.updateUI();
            //this.setLayout(new FlowLayout());

            //add(new   JLabel("" + newVal));
        }
    }
}
我觉得我的天真很可能是用了错误的方法。有什么想法吗

编辑:根据请求,我发布了附加代码

此类调用CellGUI的setValue方法:

public class GridGUI extends JPanel {

    ArrayList<CellGUI> cells = new ArrayList<CellGUI>();

    public GridGUI()
    {

        this.setLayout(new GridLayout(9,9));


        for(int i = 0; i < 81;i++)
        {
            CellGUI cell = new CellGUI();
            cells.add(cell);
            add(cell);
        }

    }


    public void updateGrid(Grid g)
    {
        for(int i = 0;i<81;i++)
        {
            cells.get(i).setValue(g.getValue(i));
        }
    }
}
我的主课叫它:

public static void main(String[] args) 
{       
    GUI gui = new GUI();
    gui.loadGrid(args[0]);
}

像这样的间歇性错误让我担心调用EDT的Swing代码,EDT是事件调度线程,它是主Swing线程,负责Swing图形和用户交互。您在哪里以及如何调用CellGUI#setValue(…)方法?它可能是关闭EDT的吗?

像这样的间歇性错误让我担心调用EDT的Swing代码,EDT是事件调度线程,它是主Swing线程,负责Swing图形和用户交互。您在哪里以及如何调用CellGUI#setValue(…)方法?可能是EDT关闭了吗?

Swing应用程序中的“间歇性/偶尔错误”。通常是由于在EDT之外更新UI造成的。有关更多详细信息,请参阅


请注意,为了更快地获得更好的帮助,请发布一个。如果我以上的怀疑是问题的原因,那么问题实际上是在未显示的代码中。

Swing应用程序中的“间歇性/偶尔错误”。通常是由于在EDT之外更新UI造成的。有关更多详细信息,请参阅


请注意,为了更快地获得更好的帮助,请发布一个。如果我以上的怀疑是问题的原因,那么问题实际上是在代码中没有显示。

因此,查看堆栈跟踪表明,错误发生在FocusManager尝试获取第一个组件时。为此,您的FocusTraversalPolicy尝试按“布局顺序”对组件进行排序,即大致按列和行排序。对于GridLayout,这应该是非常简单的。让我们看看
layoutparator
的代码。它在某些地方抛出ClassCastException:

        if (a == null) {
            // 'a' is not part of a Window hierarchy. Can't cope.
            throw new ClassCastException();
        }
a是此处的包含窗口,如果不存在此类窗口,则为
null

因此,看起来您的组件尚未在组件树中完全注册。正如其他人所说,如果您在不在AWT事件调度线程中时在GUI中进行一些更改,则可能会发生这种情况

为了避免这种情况,在调用
EventQueue.invokeLater(…)
时,将对GUI的所有更改(包括创建组件,如上面的构造函数调用)包装起来。(有时,
invokeAndWait
更有用,您也可以在SwingUtilities中使用相同的命名方法。)在您的例子中,更改非常简单:

public static void main(String[] args) 
{
    EventQueue.invokeLater(new Runnable() { public void run() {
        GUI gui = new GUI();
        gui.loadGrid(args[0]);
    }});
}

因此,查看堆栈跟踪表明,当FocusManager尝试获取第一个组件时,会发生错误。为此,您的FocusTraversalPolicy尝试按“布局顺序”对组件进行排序,即大致按列和行排序。对于GridLayout,这应该是非常简单的。让我们看看
layoutparator
的代码。它在某些地方抛出ClassCastException:

        if (a == null) {
            // 'a' is not part of a Window hierarchy. Can't cope.
            throw new ClassCastException();
        }
a是此处的包含窗口,如果不存在此类窗口,则为
null

因此,看起来您的组件尚未在组件树中完全注册。正如其他人所说,如果您在不在AWT事件调度线程中时在GUI中进行一些更改,则可能会发生这种情况

为了避免这种情况,在调用
EventQueue.invokeLater(…)
时,将对GUI的所有更改(包括创建组件,如上面的构造函数调用)包装起来。(有时,
invokeAndWait
更有用,您也可以在SwingUtilities中使用相同的命名方法。)在您的例子中,更改非常简单:

public static void main(String[] args) 
{
    EventQueue.invokeLater(new Runnable() { public void run() {
        GUI gui = new GUI();
        gui.loadGrid(args[0]);
    }});
}

我看不出有任何理由删除所有标签


您所需要做的就是使用setText(…)来更改标签的文本。不需要删除所有标签,然后再重新添加。

我看不出有任何理由删除所有标签


您所需要做的就是使用setText(…)来更改标签的文本。无需将它们全部删除,然后再重新添加。

有时,当您只打算使用一个线程时,您会得到更多,并遇到错误线程的更新问题

对我来说,它发生在应用程序启动并开始准备GUI时。 同时,鼠标位于GUI所在的屏幕位置。
GUI发现鼠标已经在其空间中,可以与后台工作异步进行。将鼠标放在屏幕的一角会使问题消失。

有时,当你只打算使用一个线程时,你会得到更多,并遇到错误线程的更新问题

对我来说,它发生在应用程序启动并开始准备GUI时。 同时,鼠标位于GUI所在的屏幕位置。
GUI发现鼠标已经在其空间中,可以与后台工作异步进行。把鼠标放在屏幕的一角,问题就消失了。

我相信它被取消了主线程;我在上面发布了附加代码。我在上面的代码中没有看到您异常的原因。对不起,我不打算浏览“附加代码”的零碎部分。这就是为什么我推荐我们可以编译和运行的SSCCE。一旦它在我的代码编辑器中被(编译),我就会更仔细地查看代码。是的,一个SSCCE需要花费相当多的精力来创建,但这是花得很好的。我打赌如果OP这样做,他会自己发现错误的;我在上面发布了附加代码。我在上面的代码中没有看到您异常的原因。对不起,我不打算浏览“附加代码”的零碎部分。