本文介绍了如何将SwingWorker的发布委派给其他方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题"可以通过以下描述.假设我们有一个密集的过程,希望在后台运行,并更新Swing JProgress栏.解决方案很简单:

My "problem" can be described by the following. Assume we have an intensive process that we want to have running in the background and have it update a Swing JProgress bar. The solution is easy:

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);
    }

}

现在假设我有各种方法花费很长时间.例如,我们有一种从服务器下载文件的方法.或另一个上传到服务器的文件.还是真的.将publish方法委派给那些方法,以便它们可以适当地更新GUI的正确方法是什么?

Now assume that i have various methods that take a long time. For instance we have a method that downloads a file from a server. Or another that uploads to a server. Or anything really. What is the proper way of delegating the publish method to those methods so that they can update the GUI appropriately?

到目前为止,我发现的是这个(假设方法"aMethod"驻留在其他软件包中):

What i have found so far is this (assume that the method "aMethod" resides in some other package for instance):

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();
            }
        }
    }

}

它可以工作,但我知道它缺少某些东西.有什么想法吗?

It works but i know it lacks something. Any thoughts?

推荐答案

(我正在更新我的答案,以使其更加清晰和概括)

(I'm updating my answer to make it more clear and generalized)

尽管您已经成功地将逻辑和表示分离开来,但是这样做的方式不适合于代码重用.Java的 PropertyChangeSupport 通过实现绑定属性,并获得了一些实质性的重用.这个想法是使用事件处理程序代替动作对象.

Although you have successfully decoupled your logic and presentation, it's not done in a way that lends itself to code reuse. Java's PropertyChangeSupport makes it easy to decouple the logic from the presentation by implementing bound properties, and get some substantial reuse. The idea is to use event handlers instead of action objects.

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

First, conceptualize the abstraction. The background work needs to "shout out" (publish) to the GUI intermittently, and the GUI needs to listen for it. Two generic classes will codify this idea:

/**
 * 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:

These classes can be used for all of your Swing widgets. For a ProgressBar, the "shout out" will be an Integer, and the return type is 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详细信息的所有信息,并且后台逻辑仍然可以控制发布(通过大喊"间接地).该代码更简洁,可读性和可重用性.

Only the listener needs to know anything about the GUI details, and the background logic still has control over publishing (indirectly, by "shouting"). This code is more terse, readable, and reusable.

我知道这个问题现在已经很老了,但是希望可以对某人有所帮助!

I realize this question is pretty old now, but hopefully it helps somebody!

这篇关于如何将SwingWorker的发布委派给其他方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 21:18