如何使用带有长时间运行的过程,应该更新发送回控制器的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 ..这将落入符合其应该如何使用。
我认为发布/过程对从推的SwingWorker数据到GUI的。 传递信息的另一种方式是通过使GUI或控制通过使用的PropertyChangeSupport和的PropertyChangeListeners 拉信息出的SwingWorker的。 考虑
- 给你的模型的PropertyChangeSupport场,
- 给它添加和删除方法的PropertyChangeListener
- 有它通知状态变化的支持对象。
- 具有的SwingWorker添加一个PropertyChangeListener模型。
- 然后在具有该模型的状态改变的通知的SwingWorker控制或视图。
- 该SwingWorker的甚至可以使用来自模型更改的信息发布/过程。
编辑
关于你提到的更新:
基本上所有具有长时间运行的方法,低层次的类将有一个的PropertyChangeSupport场。 在长时间运行的方法调用中的firePropertyChange()定期对方法的状态更新,而不是一定要报告性质的变化 - 这就是我所说的语义滥用意思!
我不建议你这样做。 记者了解到,如果被监听绑定的属性没有改变,没有任何的PropertyChangeListener(PCLS)将被即使firePC()被调用通知。 如果您需要轮询属性,那么我就不会使用PCL做到这一点。 我只想轮询它可能是由外部类轮询。
就个人而言,我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) {
}
}
}
}
}
对于下一个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
}