【问题标题】:Return Values & JProgressbar返回值和 JProgressbar
【发布时间】:2014-10-23 12:08:34
【问题描述】:

我在为 JProgressBar 设置一个值时遇到了问题,我从一个类型返回方法中获得了该值。我知道我必须做多线程,但我对这个主题真的很陌生,真的不知道如何实现它。

我尝试在代码中简要解释我的困境:

这是我的数据返回方法,用于计算字符串中的行数(例如,来自 JTextArea 或其他)。

    public static int countLines(String s) {
        int count = 0;

        String[] words = s.split("\n");

        for(String i: words) {
            count++;
        }
        return count;
    }

我要添加的是此方法中的一个方法,它将我在 JFrame 中创建的 JProgressBar 设置为值 count。例如。 setProgress(count);

这甚至可能吗?因为我尝试了几种方法,无论如何,进度条只会在返回值发出后更新。

我必须在自己的线程中运行此方法还是仅在进度设置方法中运行?还是两者都有?

干杯!

【问题讨论】:

    标签: java multithreading swing return-type jprogressbar


    【解决方案1】:

    我必须在自己的线程中运行此方法还是仅在进度设置方法中运行?还是两者兼而有之?

    两个任务应该在单独的线程中运行:业务逻辑(在这种情况下是字数)必须在后台线程中执行,而进度条更新必须在 Swing 线程中运行,也称为Event Dispatch Thread (EDT)

    IMO 最好的方法是使用SwingWorker

    • 将所有计数逻辑移至 doInBackground() 实现。
    • PropertyChangeListener 附加到swing worker 以侦听progress 属性并更新此侦听器中的进度条。
    • doInBackground 中使用setProgress() 来设置worker 的进度并触发一个属性更改事件来通知您的侦听器。

    SO 里面有很多例子,看看 标签就知道了。另外,考虑一下这个 sn-p:

    SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
    
        // Following code is performed in a background thread
    
        @Override
        protected Void doInBackground() throws Exception {
    
            String[] words = string.split(System.lineSeparator());
            int totalWords = words.length;
    
            for (int count = 0; count < totalWords; count++) {
                int progress = (count + 1) * 100 / totalWords;
                setProgress(progress);
            }
            return null;
        }
    };
    
    worker.addPropertyChangeListener(new PropertyChangeListener() {
    
        // Following code is performed in the EDT, as all event handling code is.
        // Progress bar update must be done here, NOT within doInBackground()
    
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            if ("progress".equals(evt.getPropertyName())) {
                Integer progress = (Integer)evt.getNewValue();
                progressBar.setValue(progress); 
            }
        }
    });
    

    【讨论】:

    • 这与@inquisitor 的答案非常相似。这会有所帮助,谢谢!
    • 不客气。然而,这种实现是错误的。不得在 doInBackground 方法中更新 Swing 组件。我将添加一个 sn-p 来说明我的建议。 @garlicDoge
    • 我刚刚更新了我的答案。 1) 现在进度条在右线程 (EDT) 中更新。 2)进度是一个百分比值,所以设置count作为进度条值是错误的。见sn-p。 @garlicDoge
    • 酷,我稍后会玩这个。到目前为止谢谢。对于我要设置的值:我要设置一个数值,而不是百分比。
    • 然后你可以使用进度条的setString()方法来显示字数和setProgress()来设置百分比值。通过这种方式,文本将看起来像您想要的那样,并且进度条将被正确填充。 @garlicDoge
    【解决方案2】:

    使用 SwingWorker 可以这样完成:

    public class GuiTest extends JFrame
    {
       private static final long serialVersionUID = 1L;
    
       private JProgressBar progressBar;
       private int stringLength;
    
       GuiTest()
       {
          // Length of your string
          stringLength = yourString.length();
    
          progressBar = new JProgressBar(0, stringLength);
          ProgressBarWorker worker = new ProgressBarWorker(); 
          worker.addPropertyChangeListener(new PropertyChangeListener() {
    
             @Override
             public void propertyChange(PropertyChangeEvent evt) {
                if ("progress".equals(evt.getPropertyName())) {
                   Integer progress = (Integer)evt.getNewValue();
                   progressBar.setValue(progress); 
                }
             }
          });
    
          worker.execute();
    
          this.getContentPane().add(progressBar);
          this.setVisible(true);
          this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       }
    
       private class ProgressBarWorker extends SwingWorker<Void, Void>
       {
           private void countLines(String s) {
              int count = 0;
    
              String[] words = s.split("\n");
    
              for(String i: words) {
                  count++;
                  setProgress(count);
               }
          }
    
          @Override
          protected Void doInBackground() throws Exception {
            countLines();
            return null;
          }
        }
    

    【讨论】:

    • 它的哪些方面不起作用?你得到了和以前一样的结果吗?
    • 对不起,是的,我得到了和以前一样的结果。一天前我用这种方法玩了一下(在stackoverflow上找到了类似的主题)。结果简而言之:返回类型正在运行循环,进度条没有任何反应。完成循环后,所有.setValue(count);都设置为进度条。我知道这在逻辑上是正确的,因为我不使用多个线程。
    • 我使用 SwingWorker 更新了我的代码。我不确定你在做什么,但我保持它的通用性让你继续前进。
    • 请注意,进度条值是通过doInBackground() 方法更新的,该方法用尽了事件调度线程 (EDT)。应在 EDT 中创建和更新 Swing 组件。在这种情况下,您有两个选择:1)使用SwingWorker#setProgress() 并附加PropertyChangeListener 来更新进度条值(就像我一样)。 2) 使用SwingWorker#publish() 发布中期结果,使用SwingWorker#process() 更新进度条。我给你一个 +1,因为我知道你会纠正你的答案:)
    • @dic19 谢谢,我仍在学习如何在 EDT 上正常运行!我现在会修正我的答案,感谢您的 +1。
    【解决方案3】:

    几周前我也陷入了困境。我找到了this 站点,这对我理解该主题有很大帮助。

    我只能说,建议使用 Swing-Library 启动您的 GUI 应用程序,如下所示:

        public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            MyApplication app = new MyApplication();
            app.setVisible(true);
          }
        });
      }
    

    【讨论】:

    • 我不明白你的代码和我的代码之间的区别。直接在我的主要方法中我运行我的 JFrame 扩展 GUI 类 - > new ApplicationWindow().loadContent(); 没有实例化它我猜...
    • 这都是关于事件调度线程的 - 这是所有 GUI 活动在 Swing 下运行的线程的名称。每次您想更改 GUI 上的某些内容时,都可以使用 SwingUtitlities.invokeLater() - 方法将其关闭到该线程。
    【解决方案4】:

    如果您在 JProgressBar 上调用 paintImmediately,那么您可以在您的代码在事件调度线程上运行时更新它。另一种解决方案是启动另一个线程。

    如果用户应该在计数代码运行时与 GUI 交互,那么启动工作线程(有或没有 SwingWorker)是唯一的解决方案。如果用户无论如何都必须等待,并且阻止 GUI 不是问题,那么(更简单的)paintImmediately 就可以了。

    【讨论】:

      猜你喜欢
      • 2012-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-10
      • 1970-01-01
      • 2019-03-24
      • 2014-12-13
      • 2012-04-02
      相关资源
      最近更新 更多