Java中事件上的MVC模式启用按钮
我现在正开始使用MVC模式,我正在思考一些事情。设想以下情况: 很明显,我看到了。在此视图中,用户可以选择一个文件,然后单击加载按钮。单击此加载按钮后,应立即启用我的gui中的所有其他组件,因为只要未加载任何文件,它们就被设置为禁用 我到目前为止所做的: 我已根据以下方案创建了一个视图:Java中事件上的MVC模式启用按钮,java,model-view-controller,Java,Model View Controller,我现在正开始使用MVC模式,我正在思考一些事情。设想以下情况: 很明显,我看到了。在此视图中,用户可以选择一个文件,然后单击加载按钮。单击此加载按钮后,应立即启用我的gui中的所有其他组件,因为只要未加载任何文件,它们就被设置为禁用 我到目前为止所做的: 我已根据以下方案创建了一个视图: public class GUI implements Observer { private JButton loadButton, showButton; private JComboBox name
public class GUI implements Observer {
private JButton loadButton, showButton;
private JComboBox nameBox;
private Controller controller = new Controller();
// Initialising Component and so on...
loadButton.addActionListener(... // calls the loadFile()-Method
public void update(Observable arg0, Object arg1) {
// What to do here?
}
}
还有这样一个控制器:
public class Controller extends Observable {
public void loadFile() {
// Load selected File
notifyObservers(); // <--- What should they be notified about in order to enable their component?
}
}
因此,我的问题是,我不确定在控制器上启用组件是否明智。还是最好在视图中检查文件是否已加载,然后将组件设置为已启用?控制器是控制视图的层。所以,如果你有一些规则,比如禁用按钮,控制器必须知道它 您必须遵守为按钮公开禁用/启用方法的约定 您的控制器必须根据您的规则调用此方法。您的GUI必须发送事件并由控制器进行操作
如果您这样做,您的GUI和控制器将是模块化的。当您必须将GUI框架开关更改为light client时,请使用另一个库。。。您的控制器将不会更改。当然,您不必实现任何规则,因为只有控制器知道这些规则。您不显示您的模型,但它可能知道文件是否已成功加载 所以我只想告诉大家模型已经改变了 然后视图有责任决定显示什么、启用什么按钮或其他。所以它可能有这样的逻辑
if ( model.isFileLoaded() ){
enable buttons
} else {
disable all buttons except the load
if ( model.hadError() ) {
display helpful message (and let user specify a different file)
}
}
通过这种方式,控制器不需要知道视图对模型中的哪些特定部分感兴趣,也不需要知道关于按钮和视图状态的任何信息。我可能会这样做,即关闭模型更改的所有内容。一些可观察的bean将保存该文件,而您的“控制器层”将由bean的侦听器组成,这些侦听器根据文件属性值的存在或不存在来更新按钮状态 这将使一切解耦,因为您可以轻松地添加其他按钮、删除按钮等,而无需更改代码。如果您有其他设置或清除文件属性的方法,则不需要复制并粘贴所有禁用/启用代码 因此,在上面的示例中,loadFile将加载该文件并将其设置在模型bean上。该值更改将导致在更新按钮状态的侦听器上触发PropertyChangeEvents 从我的角度来看:在直接设置按钮的基础上操作操作的额外积分。这些操作甚至可以是bean更改侦听器,只需调用setEnabled即可。但我喜欢尽可能少地将控制器代码与实际的swing组件耦合 如果这令人困惑,我可以提供示例代码好的,提供的代码 所以,我的纯方法是让所有按钮都有动作支持。。。甚至可以使用公共基类:
public abstract class FileRequiredAction extends AbstractAction
implements PropertyChangeListener {
public FileRequiredAction( String name ) {
super( name );
}
public void propertyChanged( PropertyChangeEvent event ) {
if ("file".equals( event.getName())
setEnabled( event.getNewValue() != null );
}
}
然后假设您有一个适当的模型bean,该bean带有一个触发正确事件的setFile方法,您的UI设置代码如下所示:
Action a;
a = new FileRequiredAction( "Show" ) {
public void actionPerformed( ActionEvent event ) {
// Do the showing
}
};
myBean.addPropertyChangeListener( "file", a );
JButton showButton = new JButton( a );
...repeat for other actions that require "file".
加载按钮需要做的就是调用myBean.setFile someNonNullValue,按钮将自动启用。额外的好处是,其他一些操作会清除它们再次被禁用的值
对于完整的代码,我省略了一些事情,比如在myBean.getFile已经有值的情况下确保操作初始化其启用状态,等等
希望这能让事情变得更清楚。我不会把这些架构的东西弄得太复杂。。。从我的角度来看,我认为这个案例中,您的模型只是文件及其内容。你的观点就是你的观点;-控制器是按钮/动作监听器。只要做:
class MyViewClass extends SomeSwingComponent
{
}
只有当用户在加载文件时可以执行任何操作时,您才应该考虑在观察线程中加载文件。不使用Observer类,我可以想到的一种方法如下: 公共类GUI{ 私有JButton loadButton、showButton; 专用JComboBox名称框;
loadButton.addActionListener(new Controller(GUI));
public void enableShow(boolean b) {
// enable Show button if b is true
}
public void enableName(boolean b) {
// display Name combo box if b is true
}
}
公共类控制器实现ActionListener{
私有GUI视图
public Controller(GUI v) {
this.view = v;
}
public void actionPerformed(ActionEvent pActionEvt) {
boolean status = loadFile();
if (status) {
view.enableShow(true);
view.enableName(true);
}
}
private boolean loadFile(String file) {
// loads the specified file and return true if load is successful
}
}
Swing框架负责在控制器中调用actionPerformed方法
当用户单击加载按钮时
您还可以将loadFile方法放在一个单独的类中
将作为一个榜样
控制器分段的一个优点是您可以
使用模拟框架独立地对控制器进行单元测试。
或者,您可以创建一个GUI类来实现一个接口,然后使用
依赖注入以单元测试控制器类中的逻辑 这会将视图的详细信息耦合到控制器逻辑中。没必要那样
.1:同意-一旦你从模型的角度看问题,它就会变得非常明显。
public Controller(GUI v) {
this.view = v;
}
public void actionPerformed(ActionEvent pActionEvt) {
boolean status = loadFile();
if (status) {
view.enableShow(true);
view.enableName(true);
}
}
private boolean loadFile(String file) {
// loads the specified file and return true if load is successful
}