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

相關問題