JProgressBar在循环中无法更新

3

我目前正在自学Java。我尝试了不同的东西,如JRadioButtonJcomboBox等。现在,我正在尝试使用JProgressBar,但它似乎无法正常工作。

相关代码片段:

JProgressBar progress;
JButton button;     //Fields of the class

progress=new JProgressBar(JProgressBar.HORIZONTAL,0,100);
button=new JButton("Done");    //Done from methods

progress.setValue(0);
progress.setStringPainted(true);
progress.setBorderPainted(true);  //Also done from methods

button.addActionListener(this);   //Also done from methods

当点击按钮时,我希望显示 JProgressBar 从 0% 到 100% 。下面是 actionPerformed 方法的相关部分:

public void actionPerformed(ActionEvent e)
{

        for(int i=0;i<=progress.getMaximum();i++)
        {
            progress.setValue(i);
            /*try{
                Thread.sleep(10);
            }catch(InterruptedException ex)
            {
                System.err.println("An error occured:"+ex);
                ex.printStackTrace();
            }*/
        }


        progress.setValue(progress.getMinimum());
}

我已将progressbutton都添加到一个JPanel中,并将该面板添加到一个JFrame上,然后使用setVisible(true);
问题在于,每当我按下JButton button时,JProgressBar progress并不会从0%到100%。相反,什么也没有发生。如果我取消注释try...catch块,然后按下button,程序会“冻结”一会儿,然后继续执行。这次,JProgressBar仍停留在0%,我从未看到它移动。我还尝试在try...catch块后添加repaint();,但结果相同。
我尝试添加
System.out.println(i+"");

actionPerformed中的for循环内,这将数字0到100打印在终端上。所以我确定循环运行了。
我该如何解决这个问题?
3个回答

4
  • Swing是单线程的,

  • 所有更新必须在EDT上完成,

  • 当前EDT中的所有事件将在GUI中同时绘制出来,

  • Thread.sleep用于锁定EDT中的事件执行,重绘可以在for_loop结束后绘制,在执行Thread.sleep锁定后的所有锁之后绘制。更多信息请参见Oracle手册-Swing中的并发处理EventDispatchThread


actionPerformed内部的所有事件都执行完毕后,应该执行AWT(Swing)监听器- ActionListener的输出,然后只有progress.setValue(progress.getMinimum());被执行,无论是否存在Thread.sleep

public void actionPerformed(ActionEvent e) -> progress.setValue(progress.getMinimum());

  • 使用SwingWorkerRunnable#Thread,并将progress.setValue(i);包装在invokeLater中。

  • 为了更快地获得更好的帮助,请发布一个SSCCE/MCVE,简短、可运行、可编译的示例代码。


你能修改我的 actionPerformed 方法,让我得到期望的结果吗?对我来说,浏览许多在线教程并找出如何做很困难。 - Spikatrix
你需要将通知JProgressBar的任何内容重定向到工作线程,我建议使用SwingWorker,例如java+swing+swingworker+jprogressbar - mKorbel

1

您需要将以下代码放入新线程中

public void actionPerformed(ActionEvent e)
{

    for(int i=0;i<=progress.getMaximum();i++)
    {
        progress.setValue(i);
        /*try{
            Thread.sleep(10);
        }catch(InterruptedException ex)
        {
            System.err.println("An error occured:"+ex);
            ex.printStackTrace();
        }*/
    }


    progress.setValue(progress.getMinimum());
}

类似这样的东西。
public void actionPerformed(ActionEvent e)
{
    new Thread(){
        public void run(){
            for(int i=0;i<=progress.getMaximum();i++)
            {
                progress.setValue(i);
                try{
                    sleep(10);
                }catch(InterruptedException ex)
                {
                    System.err.println("An error occured:"+ex);
                    ex.printStackTrace();
                }
            }
        }
    }.start();
    progress.setValue(progress.getMinimum());
}

未经测试,progress 实例可能应该是 final


Swing组件不是线程安全的,永远不应该在EDT上下文之外更新。考虑使用SwingWorker代替。 - MadProgrammer
这个可以运行。但是我读到swing不是线程安全的。另外,我想让JProgressBar到100%,之后用户就可以使用其他组件了。我猜我需要join()或类似的东西?(顺便说一句,我不是很熟悉java中的线程) - Spikatrix

1

就你的代码而言,看起来都是合法的。似乎不会出现任何问题,即使没有启动线程。

我建议你删除这个:

//for testing purposes
progress.setValue(progress.getMinimum());

然后将进度条的值设为50。删除线程代码,使用条件为“< 50”的for循环。

如果其他方法都失败了,这一定会有所帮助:JProgress Bar: Working!

让我知道结果。


我想在循环后将JProgressBar的值重置为零。在我的实际代码中,循环和progress.setValue(progress.getMinimum())之间需要完成一些工作。 - Spikatrix
是的,我理解了。这是必需的,我建议从测试的角度考虑 :) - TejjD

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接