我的项目是基于Java的Swing库构建的。它产生了显示我的GUI(正常工作)的EDT。
程序的入口,初始化EDT:
public final class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Start());
}
class Start implements Runnable {
private Model model = new Model();
private Controller controller = new Controller(model);
private View view = new View(controller);
@Override
public void run() {
// Initialize the view and display its JFrame...
}
}
}
}
但是,当在我的GUI中单击按钮/单选框/等时,Controller类必须对模型执行操作。
我的问题如下:
例如:
public class Controller {
public void updateModel() {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
model.somethingSomethingSomething();
}
}.execute();
}
}
public class Model {
public void somethingSomethingSomething() {
notifyListeners(); // This is going to notify whichever GUI
// is listening to the model.
// Does it have to be wrapped with Swing.invokeLater?
}
}
public class View {
// This function is called when the model notifies its listeners.
public void modelChangedNotifier() {
button.setText("THE MODEL HAS CHANGED"); // Does this occur on the EDT?
}
}
答案 0 :(得分:4)
您可以在此处阅读:Improve Application Performance With SwingWorker in Java SE 6。简而言之:所有耗时的操作,不受影响的UI必须在另一个线程中完成。要显示操作结果,您必须返回EDT。例如,如果进行数据库搜索,则应显示进度条(通常为无限)并使用SwingWorker开始搜索。要在表格中显示搜索结果,您必须在EDT中。或者,您可以使用foxtrot lib(它允许您的代码更方便Swing而无需重新设计)。 如果您的控制器代码永久更新swing小部件,您应该在EDT中执行它,或者至少在EDT中执行UI的这些更新(使用SwingUtilities.invokeLater,SwingWorker中的块处理或swing.Timer)。 所以你的样本是错误的:模型更新应该在EDT中是最新的。
答案 1 :(得分:4)
而不是从doInBackground()
,publish()
中期结果更新您的模型,而是从在EDT上执行的process()
更新您的模型。在此example中,JTable
对应于您的View
,而TableModel
对应于您的Model
。请注意,JTable
会收听自己的TableModel
。
答案 2 :(得分:-2)
一种替代方法,来自实践9.4.2中的Java Concurrency,使用“Split”或“Shared Data Model”。您可以在任何您想要的线程上更新您的业务模型,可能是长期运行的非EDT线程。但是,不要直接调用notifyListeners()并担心你所在的线程,而只需调用myComponent.repaint(),它将在EDT 上排队重绘请求。
然后,在paintComponent()
方法的某处,您明确地从模型中获取所有新数据,通常是在名为modelToView()
commentTextArea.setText(myModel.getCommentText());
fooLabel.setText(myModel.getFooText());
...
好处是线程不是一个问题,至少对某些人来说,这“有道理”,而且模型与视图很好地分离。缺点是您每次都要重置所有值。所以,如果你有100个JComponents,那就是设置的100件事。此外,视图与模型紧密耦合。
工作代码示例
@MadProgrammer和@kleopatra是正确的,如果视图直接包含正在更新的组件,则会出现“无限循环的厄运”。有关证明,请参阅
但是,如果视图与组件隔离,则可以避免无限循环。通常情况下,更高级别的视图将包含JSplitPanes,持有JScrollPanes,持有Box或更多JPanels等内容,保留实际的低级别组件。所以这个要求,IMO,并非不合理。
的工作代码为Downvoters添加了一些评论:
有些人想要击败Swing 。他们正在编写仪器控制代码或算法,只想在不担心EDT,无休止的SwingWorkers和invokeLaters的情况下完成工作。这项技术让他们完成了自己的工作。有一个重要的警告,它有效。 (就个人而言,我理解并且通常喜欢Swing,但很多人不喜欢)。
虽然Swing组件很适合MVC,但它们通常都处于微观层面。 “真实”模型不是单个String,而是几十个值。 “真正的”视图不是单个JLabel,它是许多JPanel,每个都有许多组件,结合了滚动器,分离器等。这种技术通常更适合现实世界,允许程序员自然地在更高层次上思考。 / p>
就“糟糕的做法”而言,请与Brian Goetz,Josh Bloch等人联系,这是“对权威的吸引力”,但它对我有用。 : - )