2010-05-25 49 views
5

我的“问题”可以通过以下描述。假设我们有一个密集的过程,我们希望在后台运行并更新Swing JProgress栏。解决方案很简单:如何委派的SwingWorker的发布到其他方法

import java.util.List; 

import javax.swing.JOptionPane; 
import javax.swing.JProgressBar; 
import javax.swing.SwingWorker; 


/** 
* @author Savvas Dalkitsis 
*/ 
public class Test { 

    public static void main(String[] args) { 
     final JProgressBar progressBar = new JProgressBar(0,99); 
     SwingWorker<Void, Integer> w = new SwingWorker<Void, Integer>(){ 

      @Override 
      protected void process(List<Integer> chunks) { 
       progressBar.setValue(chunks.get(chunks.size()-1)); 
      } 

      @Override 
      protected Void doInBackground() throws Exception { 

       for (int i=0;i<100;i++) { 
        publish(i); 
        Thread.sleep(300); 
       } 

       return null; 
      } 

     }; 
     w.execute(); 
     JOptionPane.showOptionDialog(null, 
       new Object[] { "Process", progressBar }, "Process", 
       JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
       null, null, null); 
    } 

} 

现在假设我有很多方法需要很长时间。例如,我们有一种从服务器下载文件的方法。或另一个上传到服务器。或者任何真的。将发布方法委派给这些方法的正确方法是什么,以便他们可以适当地更新GUI?

是我迄今发现的是这个(假设法“amethod方法”存在于其他一些包为例):

import java.awt.event.ActionEvent; 
import java.util.List; 

import javax.swing.AbstractAction; 
import javax.swing.Action; 
import javax.swing.JOptionPane; 
import javax.swing.JProgressBar; 
import javax.swing.SwingWorker; 


/** 
* @author Savvas Dalkitsis 
*/ 
public class Test { 

    public static void main(String[] args) { 
     final JProgressBar progressBar = new JProgressBar(0,99); 
     SwingWorker<Void, Integer> w = new SwingWorker<Void, Integer>(){ 

      @Override 
      protected void process(List<Integer> chunks) { 
       progressBar.setValue(chunks.get(chunks.size()-1)); 
      } 

      @SuppressWarnings("serial") 
      @Override 
      protected Void doInBackground() throws Exception { 

       aMethod(new AbstractAction() { 

        @Override 
        public void actionPerformed(ActionEvent e) { 
         publish((Integer)getValue("progress")); 
        } 
       }); 

       return null; 
      } 

     }; 
     w.execute(); 
     JOptionPane.showOptionDialog(null, 
       new Object[] { "Process", progressBar }, "Process", 
       JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
       null, null, null); 
    } 

    public static void aMethod (Action action) { 
     for (int i=0;i<100;i++) { 
      action.putValue("progress", i); 
      action.actionPerformed(null); 
      try { 
       Thread.sleep(300); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

} 

它的工作原理,但我知道这没啥事。有什么想法吗?

回答

0

也许做每个长方法的SwingWorker。每个SwingWorker都有自己的进度级别。

每个SwingWorker的更新doInBackground方法在它自己的进步程度,然后调用发布。在处理方法里面,所以在EDT里面,每个SwingWorker都会读取它的进度级别,并更新模型和视图的一般进度条。

+0

如果我们只有一种可怕的长方法会怎样?我不是downvoting,但这不是一个解决方案 – Xorty 2010-05-25 21:33:12

+0

如果我的答案不好,你可以downvoting。但我读到了这样一个问题:“例如我们有一种方法可以从服务器上下载文件,或者另一种方法上传到服务器,或者任何其他方式”......所以我想有很多方法,而不仅仅是一个可怕的方法长 ? – Istao 2010-05-26 04:46:32

+0

我遇到的问题是可以从代码的不同部分调用这些方法。而他们需要刷新的gui元素并不总是相同的。通过我的解决方案,我可以创建一个通用方法,并且每次创建一个新的摆动工人时,我都可以在我的GUI上确定工艺方法。 – 2010-05-26 19:35:25

0

我面临着类似的问题。以下是我发现的,也许真正正确的答案没有,但让我们试试看:

  • 如果我们有很多的迭代,我们可以在doInBackGround()方法更新进度条。的JProgressBar是我们的SwingWorker延伸SwingWorker类(所以是的,我们使用自定义)
  • 如果我们没有重复,我们不能中断方法,所花的时间很多来完成,我们可以拧整个事情,做的构造函数参数它与大多数人一样(所以我们的进度条并不是线性过程,而是在部分工作完成后刷新它的值)。坏消息是,如果我们的方法是唯一的工作者(f.e.在后台发送电子邮件)进度条将变为突然全满。不是很漂亮但是,让我们在第三个选项
  • 这个一看可能会觉得很疯狂,业绩放缓,这都是因为我们的应用程序必须是看中。那么我们来看看需要很长时间才能完成的方法的源代码。我们覆盖它并粘贴完全相同的代码,但我们再添加一个参数 - 猜猜看,是JProgressBar。在方法内部,我们创建的Thread将运行,直到某个布尔参数(标志指示方法最终完成)设置为true。线程将继续更新JProgressBar 合理的间隔。最大的问题是假设,合理的区间是多少。我们应该进行一些测试并估计间隔的价值。

在第三点,我描述了如何从完成一些不是迭代的任务(至少不是在我们的Java代码中)并且不能被中断的方法中执行Thread。线程更新作为方法参数给出的JProgressBar。这一点,但是,definitelly慢作为纯粹的方法调用

10

(我更新我的回答,使之更加清晰和广义)

虽然你已经成功地分离你的逻辑和表示,这不是在做一种有助于代码重用的方式。Java的PropertyChangeSupport通过实现bound properties可以很容易地将演示逻辑与逻辑分离,并获得一些实质性的重用。这个想法是使用事件处理程序而不是操作对象。

首先,概念化抽象。后台工作需要间歇性地“发出”(发布)到GUI,并且GUI需要监听它。两个通用类将编码这个想法:

/** 
* Wrapper for the background logic. 
* 
* <T> return type 
* <S> intermediary type (the "shout out") 
*/ 
public static abstract class LoudCall<T, S> implements Callable<T> { 

    private PropertyChangeSupport pcs; 
    private S shout; 

    public LoudCall() { 
     pcs = new PropertyChangeSupport(this); 
    } 

    public void shoutOut(S s) { 
     pcs.firePropertyChange("shoutOut", this.shout, 
       this.shout = s); 
    } 

    public void addListener(PropertyChangeListener listener) { 
     pcs.addPropertyChangeListener(listener); 
    } 

    public void removeListener(PropertyChangeListener listener) { 
     pcs.removePropertyChangeListener(listener); 
    } 

    @Override 
    public abstract T call() throws Exception; 
} 

/** 
* Wrapper for the GUI listener. 
* 
* <T> return type 
* <S> intermediary type (the "shout out" to listen for) 
*/ 
public static abstract class ListenerTask<T, S> extends SwingWorker<T, S> 
     implements PropertyChangeListener { 

    private LoudCall<T, S> aMethod; 

    public ListenerTask(LoudCall<T, S> aMethod) { 
     this.aMethod = aMethod; 
    } 

    @Override 
    protected T doInBackground() throws Exception { 
     aMethod.addListener(this); 
     return aMethod.call(); 
    } 

    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     if ("shoutOut".equals(evt.getPropertyName())) { 
      publish((S)evt.getNewValue()); 
     } 
    } 

    @Override 
    protected abstract void process(List<S> chunks); 
} 

这些类可以用于所有的Swing小部件。对于一个进度条,在“喊出”将是一个整数,返回类型为void:

public class ProgressExample { 
    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
    @Override 
    public void run() { 

     // 1. setup the progress bar 
     final JProgressBar progressBar = new JProgressBar(0, 99); 

     // 2. Wrap the logic in a "Loud Call" 
     LoudCall<Void, Integer> aMethod = new LoudCall<Void, Integer>() { 
      @Override 
      public Void call() throws Exception { 
       for (int i = 0; i < 100; i++) { 
        // "i have an update for the GUI!" 
        shoutOut(i); 
        Thread.sleep(100); 
       } 
       return null; 
      } 
     }; 

     // 3. Run it with a "Listener Task" 
     (new ListenerTask<Void, Integer>(aMethod) { 
      @Override 
      protected void process(List<Integer> chunks) { 
       progressBar.setValue(chunks.get(chunks.size() - 1)); 
      } 
     }).execute(); 

     // 4. show it off! 
     JOptionPane.showOptionDialog(null, 
      new Object[] { "Process", progressBar }, "Process", 
      JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
      null, null, null 
     ); 
    } 
     }); 
    } 
} 

只有听众需要了解的GUI任何细节,背景逻辑仍然拥有公开控制(间接地通过“喊”)。这段代码更加简洁,可读,可重用。

我意识到这个问题现在很老了,但希望它可以帮助别人!

+1

很难低估这个答案是多么有用。它奖励重读! – Arvanem 2012-09-15 13:27:59

相关问题