5

私の「問題」は次のように説明できます。バックグラウンドで実行したい集中的なプロセスがあり、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();
            }
        }
    }

}

それは機能しますが、何かが欠けていることは知っています。何かご意見は?

4

3 に答える 3

10

(より明確で一般化するために、回答を更新しています)

ロジックとプレゼンテーションの分離に成功しましたが、コードの再利用に適した方法で分離されていません。Java のPropertyChangeSupportを使用すると、バインドされたプロパティを実装することでプレゼンテーションからロジックを簡単に切り離し、実質的に再利用できます。アイデアは、アクション オブジェクトの代わりにイベント ハンドラーを使用することです。

まず、抽象化を概念化します。バックグラウンド作業は断続的に GUI に「叫ぶ」(発行する) 必要があり、GUI はそれをリッスンする必要があります。2 つのジェネリック クラスがこの考え方を体系化します。

/**
 * 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 の場合、「叫び声」は 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 の詳細について知る必要があるのはリスナーだけであり、バックグラウンド ロジックは引き続き公開を制御します (間接的に「叫ぶ」ことによって)。このコードは、より簡潔で読みやすく、再利用可能です。

この質問はかなり古いものだと思いますが、誰かの役に立てば幸いです!

于 2011-07-26T18:35:00.513 に答える
0

おそらく、長いメソッドごとに SwingWorker を実行します。各 SwingWorker には独自の進捗レベルがあります。

各 SwingWorker は、doInBackground メソッド中に独自の進捗レベルを更新してから、publish を呼び出します。プロセス メソッド内、つまり EDT 内で、各 SwingWorker は進行状況レベルを読み取り、モデルとビジョンの一般的な進行状況バーを更新します。

于 2010-05-25T21:14:55.083 に答える
0

同様の問題に直面しました。これが私が見つけたものです。おそらく本当に正しい答えはありませんが、試してみましょう:

  • doInBackGround()繰り返しが多い場合は、メソッドで進行状況バーを更新できます。JProgressBar は、SwingWorker を拡張する SwingWorker のコンストラクタ パラメータです (そうです、カスタムを使用します)。
  • 反復がなく、完了するのに多くの時間がかかるメソッドを中断できない場合は、すべてを台無しにして、ほとんどの人と同じように実行できます (したがって、進行状況バーには線形プロセスはありませんが、部分的な後に値が更新されるだけです)仕事は終わった)。悪いニュースは、ワーカーが行う唯一のメソッド (fe がバックグラウンドで電子メールを送信する) の場合、プログレス バーが突然いっぱいになることです。あまり良くありませんが、3番目のオプションを見てみましょう
  • これは非常にクレイジーで、パフォーマンスが低下する可能性があります。これはすべて、アプリが空想的でなければならないためです。それでは、完了するまでに時間がかかるメソッドのソースコードに取り掛かりましょう。それをオーバーライドしてまったく同じコードを貼り付けますが、もう 1 つのパラメーターを追加します。メソッド内で、何らかのブール値パラメーター (メソッドが最終的に完了したことを示すフラグ) が true に設定されるまで実行される Thread を作成します。スレッドは、妥当な間隔で JProgressBar を更新し続けます。最大の問題は、妥当な間隔はどれくらいかを想定することです。いくつかのテストを行い、間隔の値を推定する必要があります。

3 番目のポイントでは、(少なくとも Java コードでは) 反復的ではなく、中断できないタスクを完了するメソッドから Thread を実行する方法について説明しました。スレッドは、メソッド パラメータとして指定された JProgressBar を更新します。ただし、これは純粋なメソッド呼び出しとして明らかに遅くなります

于 2010-05-25T21:31:33.967 に答える