![](/img/trans.png)
[英]How are the publish() and process() methods on SwingWorker properly used?
[英]How to delegate SwingWorker's publish to other methods
我的“問題”可以用以下描述。 假設我們有一個密集的過程,我們希望在后台運行並讓它更新一個Swing JProgress欄。 解決方案很簡單:
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();
}
}
}
}
它有效,但我知道它缺乏一些東西。 有什么想法嗎?
(我正在更新我的答案,使其更清晰和概括)
雖然您已經成功地解耦了邏輯和表示,但它並沒有以適合代碼重用的方式完成。 Java的PropertyChangeSupport通過實現綁定屬性 ,可以輕松地將邏輯與表示分離,並獲得一些實質性的重用。 我們的想法是使用事件處理程序而不是操作對象。
首先,概念化抽象。 后台工作需要間歇性地“喊出”(發布)到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小部件。 對於ProgressBar,“shout out”將是一個Integer,返回類型為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細節的任何信息,並且后台邏輯仍然可以控制發布(間接地,通過“喊叫”)。 此代碼更簡潔,可讀且可重用。
我意識到這個問題現在已經很老了,但希望它對某人有所幫助!
也許為每個長方法做一個SwingWorker。 每個SwingWorker都有自己的進度級別。
每個SwingWorker在doInBackground方法期間更新它自己的進度級別,然后調用publish。 在流程方法內部,所以在EDT內部,每個SwingWorker都會讀取它的進度級別,並更新模型和願景的一般進度條。
我遇到了類似的問題。 這是我發現的,也許真正正確的答案缺乏,但讓我們嘗試一下:
doInBackGround()
方法中更新進度條。 JProgressBar是SwingWorker的構造函數參數,它擴展了SwingWorker(所以是的,我們使用自定義) 在第三點中,我描述了如何從方法執行Thread,該方法完成了一些非迭代的任務(至少在我們的Java代碼中沒有)並且不能被中斷。 線程更新作為方法參數給出的JProgressBar。 然而,這是純方法調用的定義較慢
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.