MVC - 的SwingWorker有一个长期运行的过程,应该更新视图(MVC - SwingWo

2019-08-01 05:05发布

如何使用带有长时间运行的过程,应该更新发送回控制器的SwingWorker时获得的视图从模型中分离?

  • 我可以使用SwingWorkers doInBackground()以保持EDT响应通过调用如model.doLongProcess()从那里太棒了!

  • 我有这个问题正在试图找回数据的过程完成之前,更新与进步的观点..

  • 我知道我可以通过使用采用找回数据SwingWorkers publish()方法,但是这个我觉得力我写的代码doLongProcess()内的方法doInBackground()


作为参考的MVC实现我有一个看起来有点像这样:

http://www.leepoint.net/notes-java/GUI/structure/40mvc.html

/ structure/calc-mvc/CalcMVC.java -- Calculator in MVC pattern.
// Fred Swartz -- December 2004

import javax.swing.*;

public class CalcMVC {
    //... Create model, view, and controller.  They are
    //    created once here and passed to the parts that
    //    need them so there is only one copy of each.
    public static void main(String[] args) {

        CalcModel      model      = new CalcModel();
        CalcView       view       = new CalcView(model);
        CalcController controller = new CalcController(model, view);

        view.setVisible(true);
    }
}

我有一个模型类,它封装了一些其它类的共同形式简单的接口控制器。

我真的不希望有移动所有/一些/任何从这些类到控制器的代码 - 它不属于那里。


更新:

下面是我采用的方法-它不是最干净的解决方案,它可以被视为滥用PropertyChangeSupport ..在语义层面。

基本上所有具有长时间运行的方法,低层次的类将有一个propertyChangeSupport场。 在长时间运行的方法调用firePropertyChange()定期对方法的状态更新,而不是一定要报告性质的变化-这就是我所说的语义滥用意思!

然后它包装的低级别类Model类中捕获这些事件,并发出了自己的高级firePropertyChange ..该controller可以监听...

编辑:

为了澄清,当我打电话中的firePropertyChange(propertyName的,属性oldValue,NEWVALUE);

  • propertyName的--->我滥用propertyName的代表一个topicname
  • 属性oldValue = NULL
  • NEWVALUE =我要广播的消息

然后在模型或其中的PropertyChangeListener曾经可以辨别基于该topicname消息。

所以基本上㈣弯曲系统使用它像一个发布 - 订阅....


我想代替上述的方法,我可以根据该进度字段添加到低级类,得到更新,然后中的firePropertyChange ..这将落入符合其应该如何使用。

Answer 1:

我认为发布/过程对从的SwingWorker数据到GUI的。 传递信息的另一种方式是通过使GUI或控制通过使用的PropertyChangeSupport和的PropertyChangeListeners 信息出的SwingWorker的。 考虑

  • 给你的模型的PropertyChangeSupport场,
  • 给它添加和删除方法的PropertyChangeListener
  • 有它通知状态变化的支持对象。
  • 具有的SwingWorker添加一个PropertyChangeListener模型。
  • 然后在具有该模型的状态改变的通知的SwingWorker控制或视图。
  • 该SwingWorker的甚至可以使用来自模型更改的信息发布/过程。

编辑
关于你提到的更新:

基本上所有具有长时间运行的方法,低层次的类将有一个的PropertyChangeSupport场。 在长时间运行的方法调用中的firePropertyChange()定期对方法的状态更新,而不是一定要报告性质的变化 - 这就是我所说的语义滥用意思!

我不建议你这样做。 记者了解到,如果被监听绑定的属性没有改变,没有任何的PropertyChangeListener(PCLS)将被即使firePC()被调用通知。 如果您需要轮询属性,那么我就不会使用PCL做到这一点。 我只想轮询它可能是由外部类轮询。



Answer 2:

就个人而言,我SwingWorker我想创建一个公开publish的方法,我的实例传递SwingWorker在长时间运行的模型方法。 这样的模式推动更新控制( SwingWorker ),然后推到视图。

这里有一个例子 - 我把一切都成一个文件(用于运行简单),但我想像通常你必须单独的文件/包这些东西。

编辑

解耦控制模型,你必须有模型的观察者。 我想实现一个ProgressListener继承ActionListener 。 该模型只是通知所有注册ProgressListener说已经取得了进展。

import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class MVCSwingWorkerExample {

    public static void main(String[] args) {
        CalcModel      model      = new CalcModel();
        CalcView       view       = new CalcView();
        CalcController controller = new CalcController(model, view);
    }

    //Model class - contains long running methods ;)
    public static class CalcModel{

        //Contains registered progress listeners
        ArrayList<ActionListener> progressListeners = new ArrayList<ActionListener>();
        //Contains model's current progress
        public int status;

        //Takes in an instance of my control's Swing Worker
        public boolean longRunningProcess(MVCSwingWorkerExample.CalcController.Worker w){
            for(int i = 0; i < 60; i++){
                try {
                    //Silly calculation to publish some values
                    reportProgress( i==0 ? 0 : i*100/60);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Whowsa!");
                    e.printStackTrace();
                }
            }
            return true;
        }

        //Notify all listeners that progress was made
        private void reportProgress(int i){
            status = i;
            ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_FIRST, null);
            for(ActionListener l : progressListeners){
                l.actionPerformed(e);
            }
        }

        //Standard registering of the listeners
        public void addProgressListener(ActionListener l){
            progressListeners.add(l);
        }

        //Standard de-registering of the listeners
        public void removeProgressListener(ActionListener l){
            progressListeners.remove(l);
        }
    }

    //View Class - pretty bare bones (only contains view stuff)
    public static class CalcView{
        Box display;
        JButton actionButton;
        JLabel progress;

        public void buildDisplay(){
            display = Box.createVerticalBox();
            actionButton = new JButton("Press me!");
            display.add(actionButton);

            progress = new JLabel("Progress:");
            display.add(progress);
        }

        public void start(){
            final JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(display);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    }

    public static class CalcController{
        CalcModel model;
        CalcView view;

        public CalcController(CalcModel model, CalcView view){
            this.model = model;
            this.view = view;

            //Build the view
            view.buildDisplay();

            //Create an action to add to our view's button (running the swing worker)
            ActionListener buttonAction = new ActionListener(){
                @Override
                public void actionPerformed(ActionEvent e) {
                    Worker w = new Worker();
                    w.execute();
                }
            };
            view.actionButton.addActionListener(buttonAction);

            //Start up the view
            view.start();

        }

        //Notified when the Model updates it's status
        public class ProgressListener implements ActionListener{
            Worker w;

            public ProgressListener(Worker w){
                this.w = w;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                CalcModel model = (CalcModel)e.getSource();
                w.publishValue(model.status);
            }

        }


        //The worker - usually part of the control
        public class Worker extends SwingWorker<Boolean, Integer>{

            public Worker(){
                //Register a listener to pay attention to the model's status
                CalcController.this.model.addProgressListener(new ProgressListener(this));
            }

            @Override
            protected Boolean doInBackground() throws Exception {
                //Call the model, and pass in this swing worker (so the model can publish updates)
                return model.longRunningProcess(this);
            }

            //Expose a method to publish results
            public void publishValue(int i){
                publish(i);
            }

              @Override
              protected void process(java.util.List<Integer> chunks){
                  view.progress.setText("Progress:" + chunks.get(chunks.size()-1) + "%");
              }

             @Override
               protected void done() {
                   try {
                       view.progress.setText("Done");
                   } catch (Exception ignore) {
                   }
               }
        }
    }

}


Answer 3:

对于下一个Swing长时间运行的过程中,你必须创建一个新的线程用于该目的,所以当这个过程完成,那么你就必须更新你的MVC“Swing线程”里面,记得只有一个为每个应用程序。

试着找到一种方法,让知道你的应用是处理的用户,并且不允许他为“正片叠底”再一次,直到处理完毕。

public class CalcController {

////////////////////////////////////////// inner class MultiplyListener
/**
 * When a mulitplication is requested. 1. Get the user input number from the
 * View. 2. Call the model to mulitply by this number. 3. Get the result
 * from the Model. 4. Tell the View to display the result. If there was an
 * error, tell the View to display it.
 */
class MultiplyListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {
        final String userInput = m_view.getUserInput();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    m_model.multiplyBy(userInput);
                } catch (NumberFormatException nfex) {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            m_view.showError("Bad input: '" + userInput + "'");
                        }
                    });
                }
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        m_view.setTotal(m_model.getValue());
                    }
                });
            }
        }).start();

    }
}//end inner class MultiplyListener

}


文章来源: MVC - SwingWorker with a long running process that should update the view