So. I've made an application. It has a mainFrame which creates two panels, one holding my wizard-alike buttons (e.g. Next, Previous) and one cardLayout, enabling me to go to either next or previous panel, which I declare in the mainFrame. By having an interface, Verifiable, I'm able to override every window-class a isDataValid()-function which decides whether next and prev buttons should call for cardlayouts flip functions.
The source code can be seen below:
public class MainFrame {
private static void createAndShowGUI() {
JFrame frame = new JFrame("Dimensions helper");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
final JPanel contentPane = new JPanel();
contentPane.setLayout(new CardLayout(5, 5));
Window1 win1 = new Window1();
contentPane.add(win1);
Window2 win2 = new Window2();
contentPane.add(win2);
Window3 win3 = new Window3();
contentPane.add(win3);
Window4 win4 = new Window4();
contentPane.add(win4);
Window5 win5 = new Window5();
contentPane.add(win5);
Window6 win6 = new Window6();
contentPane.add(win6);
JPanel buttonPanel = new JPanel();
final JButton previousButton = new JButton("< PREVIOUS");
previousButton.setEnabled(false);
final JButton nextButton = new JButton("NEXT >");
final JButton cancelButton = new JButton("CANCEL");
buttonPanel.add(cancelButton);
buttonPanel.add(previousButton);
buttonPanel.add(nextButton);
previousButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
CardLayout cardLayout = (CardLayout) contentPane.getLayout();
cardLayout.previous(contentPane);
Component[] contents = contentPane.getComponents();
nextButton.setText("NEXT");
for(Component component : contents) {
if(component instanceof Verifiable && component.isVisible()) {
Verifiable window = (Verifiable)component;
if(window.getIdentifier().equals("FIRST")) {
previousButton.setEnabled(false);
}
break;
}
}
}
});
nextButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
Verifiable verifiable = null;
Component[] contents = contentPane.getComponents();
for(Component component : contents) {
if(component.isVisible() && component instanceof Verifiable) {
verifiable = (Verifiable)component;
}
}
if(verifiable != null && verifiable.isDataValid()) {
CardLayout cardLayout = (CardLayout) contentPane.getLayout();
cardLayout.next(contentPane);
previousButton.setEnabled(true);
}
}
});
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
});
frame.add(contentPane);
frame.add(buttonPanel, BorderLayout.PAGE_END);
frame.setSize(400, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
public class Window1 extends JPanel implements Verifiable {
public static final String IDENTIFIER = "FIRST";
JTextField txtUsername = new JTextField();
JPasswordField txtPassword = new JPasswordField();
Database db = new Database();
public Window1() {
init();
}
private void init() {
JLabel lblUsername = new JLabel("Username:", JLabel.CENTER);
lblUsername.setBounds(10, 91, 135, 77);
txtUsername = new JTextField();
txtUsername.setBounds(155, 116, 188, 27);
JLabel lblPassword = new JLabel("Password:", JLabel.CENTER);
lblPassword.setBounds(0, 163, 149, 77);
txtPassword = new JPasswordField();
txtPassword.setBounds(155, 188, 188, 27);
setLayout(null);
add(lblUsername);
add(txtUsername);
add(lblPassword);
add(txtPassword);
String title = "Log in";
setBorder(BorderFactory.createTitledBorder(title));
}
@Override
public boolean isDataValid() {
String username = new String(txtUsername.getText());
String password = new String(txtPassword.getPassword());
try {
Database conn = di.getDatabaseConnection(username, password);
di.getTest(conn);
return true;
} catch (LoginFailedException e) {
JOptionPane.showMessageDialog(this, "Something went wrong",
"Error", JOptionPane.ERROR_MESSAGE);
return false;
}
}
@Override
public String getIdentifier() {
return IDENTIFIER;
}
}
interface Verifiable {
boolean isDataValid();
String getIdentifier();
}
However. I want to redo this application into a Model-View-Controller architecure, meaning that I no longer have any validation code in the Window-classes (i.e. the views). Therefore I've devided the old Window-classes into a WinView-class (Win1) respectively an interface (Interface1) and a Model to verify the data.
public class Win1 extends JPanel implements Interface1 {
JTextField txtUsername = new JTextField();
JPasswordField txtPassword = new JPasswordField();
public Win1() {
JLabel lblUsername = new JLabel("Username:", JLabel.CENTER);
txtUsername = new JTextField();
JLabel lblPassword = new JLabel("Password:", JLabel.CENTER);
txtPassword = new JPasswordField();
setLayout(null);
add(lblUsername);
add(txtUsername);
add(lblPassword);
add(txtPassword);
String title = "Log in";
setBorder(BorderFactory.createTitledBorder(title));
}
@Override
public String getUsername() {
return txtUsername.getText();
}
@Override
public String getPassword() {
String password = new String(txtPassword.getPassword());
return password;
}
}
public interface Interface1 {
String getUsername();
String getPassword();
}
public class Model {
Model() {
}
}
My MainFrame looks the same so far. I get stuck when I'm trying to plan my new architecture. How should I bind the MVC architecture? I'm very happy with how my next and previous buttons work at the moment, is it possible to somehow still use them like I currently do?
The first thing I want to say is, as far as I understand the MVC methodology (I could be wrong), anything that directly influences how the View itself behaves should remain in the View (like enabling / disabling buttons). Anything else (functional methods) should be handled by a controller.
I would use the Singleton Pattern. Have a class called
MainController
and use the singleton pattern on that class. Then in yourMainView
, have a global variable of an instance of theMainController
calledcontroller
. Then in the controller have a method ofisDataValid(String username, String password)
and callboolean valid = controller.isDataValid(txtUsername.getText(), new String(txtPassword.getPassword()));
. The same thing could be done with any functional methods. I would also point out that controllers are the only ones which should have any connection to the database for the MVC methodology to work. I hope this helps.