Reputation: 865
I went through plenty of articles and (imo too complex) examples of how to separate GUI from the rest of the program logic in Java, and to be completely honest, I still have no clue.
Could someone give me a hint of how to do it on this simple example, with just one button?
Let's say there is a button in the Window class:
JButton firstButton = new JButton("My first button");
btnCreateProject.setBounds(100, 100, 80, 30);
frame.getContentPane().add(firstButton);
and let's say this button would call a constructor of the Employee class. So, I could do it like this:
JButton firstButton = new JButton("My first button");
firstButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
.....constructor calling, and rest of the code....
}
});
btnCreateProject.setBounds(100, 100, 80, 30);
frame.getContentPane().add(firstButton);
But this is exactly what I DO NOT want. I want my Window class to be just pure GUI, with buttons, radio boxes, and other stuff, with it's properties. Nothing more. I would like to have all the listeners in another class, let's say Controller, and from this class I would call all the needed methods.
Could someone give me an example of how to do it with this simple button? Thank you very much.
Upvotes: 4
Views: 10885
Reputation: 455
What you should do is write a method where appropriate to access datamodel (supposing you have one) and do the work there, and just call the method from button click.
firstButton.addActionListener(e -> logicClass.addEmployeeToFirm());
or you could event write a custom Listener that would call/do that logic (a class that implements ActionListener).
Upvotes: 1
Reputation: 1184
You could always just implement something like:
Controller.handleActionEvent(ActionEvent e);
and delegate your button click to that. And if you don't want your controller to know about Swing, you could always create some interface and wrap the Swing ActionEvent in some kind of event of your creation.
Or just implement an Observer (Listener) pattern in the UI class (or you could have a "registry" class that sits in the middle). When the button is clicked, the UI would delegate the event to all registered listeners (implementing some interface that you define). So the Controller (client) would just tell the UI "notify me when Button X is clicked" and the UI would just delegate to all interested parties.
So, the UI wouldn't need to know who to call explicitly. But the controller would have to know who he wants to listen to.
Upvotes: 1
Reputation: 1776
I had the same issue with my projects, but finally I decided to use a concept I learned in android programming. Here is how it works:
I add an identified to my objects (for buttons, menus, etc I use setActionCommand method) so I can use the same identifier for the same operations for different components.
All my objects call one controller with their getActionCommand as cmd and the object itself. Within the controller, I control the cmd and call the proper method from it.
It is much more easier to control the GUI and the rest of the program.
It worked for me this way like charm.
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
/**
*
* @author Pasban
*/
public class ActionListenerTest {
public static void main(String[] args) {
JButton b1 = new JButton("My first button");
JButton b2 = new JButton("My first button");
b1.setActionCommand("BTN_1");
b2.setActionCommand("BTN_2");
//put this in another class
ActionListener controller = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand().toUpperCase().trim();
System.out.println(cmd);
//System.out.println(e.getSource()); // cast to anything you have in your mind about the caller
if (cmd.equals("BTN_1")) {
System.out.println("BUTTON 1 is clicked");
} else if (cmd.equals("BTN_2")) {
System.out.println("BUTTON 2 is clicked");
}
}
};
b1.addActionListener(controller);
b2.addActionListener(controller);
JDialog frame = new JDialog();
frame.setSize(new Dimension(300, 300));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
frame.getContentPane().setLayout(new FlowLayout());
frame.getContentPane().add(b1);
frame.getContentPane().add(b2);
frame.setVisible(true);
}
}
Upvotes: 1
Reputation: 35011
Inside your controller, create a method
Action get[Operation]Handler(); //Fill in [Operation] will what your button functionality should do
which returns an inner class that implements the proper functionality.
Then, in your GUI code, do
JButton b = ...;
b.setAction(controller.get[Operation]Handler());
Upvotes: 0
Reputation: 180161
You are drawing a distinction of degree, not kind. Your GUI code cannot be completely separated from program logic, else it would be a static work of modern art.
The various kinds of AWT/Swing listener classes do separate GUI from logic, into altogether separate classes. You do not, however, need to implement listeners as anonymous inner classes; perhaps it would feel better to you to implement them as ordinary, top-level classes instead. Alternatively, you might find that it suits you to model program behaviors via classes implementing the Action
interface. Then you can assign those behaviors to controls (buttons, menu items) via the controls' setAction()
method.
Any way around, however, the code that sets up your GUI has to somehow know about both the GUI components and the logic that needs to be hooked up to them, else there's no way it could do its job.
Upvotes: 3
Reputation: 5168
This is a constructor for a new class:
new ActionListener() {
public void actionPerformed(ActionEvent e) {
.....constructor calling, and rest of the code....
}
}
You could make a class like:
public class ActionListenerTest implements ActionListener { ... }
Then make something like this:
firstButton.addActionListener(new ActionListenerTest());
Upvotes: 1