I'm currently creating an application in Java with Swing and using the MVC pattern, however I've run into somewhat of a concern.
The problem I'm facing is that the application will have multiple panels, such as a panel for logging in etc. So I decided to do the logical thing and use a Card Layout. However what I would like to do is have a model, view and controller for each panel so the code is tidier.
For example I would like to have a view, model and controller for the login panel. This works fine but what I can't seem to be able to achieve elegantly is changing the panel on the card layout when required e.g. When a user has logged in successfully there is no longer a need to display the login panel.
One solution I came up with (using an idea from this topic: How can I switch between JPanels in a CardLayout using MVC?) was to use a model for the system, which contained the possible states of the system. This would allow the class containing the Card layout to listen for any changes in the system state model. However this would mean that controllers such as the one used for logging in would have to interact with this system model (in order for the Card layout to be changed) as well as the model for logging in (to verify user credentials). I'm not sure if a controller referencing multiple models is good practice in terms of MVC.
Hopefully what I'm trying to achieve has been made clear, any help is appreciated.
View.java
package test;
import java.awt.CardLayout;
import java.awt.Container;
import javax.swing.JFrame;
/*
* Will listen for changes in the model's states
* and update cards accordingly.
*/
public class View {
private JFrame frame;
private CardLayout cards;
public View(){
cards = new CardLayout();
frame = new JFrame();
frame.setSize(500, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setVisible(true);
frame.getContentPane().setLayout(cards);
}
public Container getContentPane(){
return frame.getContentPane();
}
public static void main(String[] args) {
View view = new View();
Model stateModel = new Model();
Controller controller = new Controller(stateModel, view);
LoginView loginView = new LoginView();
LoginModel loginModel = new LoginModel();
LoginController loginController = new LoginController(stateModel, loginModel, loginView);
view.getContentPane().add(loginView.getPanel(), Model.States.STATE1.toString());
view.cards.show(view.getContentPane(), Model.States.STATE1.toString());
}
}
Model.java
package test;
/*
* Notify's listeners when the state has been changed.
*
*/
public class Model {
public String currentState = "";
public Model(){
//Default state.
this.currentState = States.STATE1.toString();
}
public void changeState(States state){
currentState = state.toString();
System.out.println("State has been changed.");
}
public String getCurrentState(){
return this.currentState;
}
public enum States{
STATE1, STATE2, STATE3;
}
}
Controller.java
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/*
* This class would be used for things such as the menu bar etc.
*/
public class Controller implements ActionListener{
private Model model;
private View view;
public Controller(Model model, View view){
this.model = model;
this.view = view;
}
@Override
public void actionPerformed(ActionEvent e) {
//...
}
}
LoginView.java
package test;
import javax.swing.JButton;
import javax.swing.JPanel;
public class LoginView {
private JPanel panel;
private JButton loginButton;
public LoginView(){
panel = new JPanel();
loginButton = new JButton("Login");
panel.add(loginButton);
}
public JButton getLoginButton(){
return this.loginButton;
}
public JPanel getPanel(){
return this.panel;
}
}
LoginModel.java
package test;
public class LoginModel {
private String username, password;
public LoginModel(){
}
public boolean validateCredentials(){
//Access database and validate credentials
return true;
}
}
LoginController.java
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LoginController implements ActionListener{
private LoginView view;
private LoginModel loginModel;
private Model stateModel;
public LoginController(Model stateModel, LoginModel loginModel, LoginView view){
this.view = view;
this.loginModel = loginModel;
this.stateModel = stateModel;
view.getLoginButton().addActionListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == view.getLoginButton()){
if(loginModel.validateCredentials()){
//A change of state would show a different card in the GUI view.
stateModel.changeState(Model.States.STATE2);
}else{
//Don't change the GUI's state.
}
}
}
}