What is the difference between having common or separate actionPerfomed method - java

Please help me understanding the difference between adding the action listener to JComponent in following two approaches.
First Method: Implementing actionListener to my class and adding the common actionPerformed method which choose selection based on the events
class Test implements ActionListener
{
JButton jbutton = null;
public Test(){
jbutton = new JButton();
jbutton.addActionListener(this);
}
public void actionPerformed(ActionEvent e){
//Perform operation here;
}
}
Second Method: Defining the action listener for individual JComponent.
JButton jbutton = new JButton();
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
//Perform operation here
}
});
What is the difference between these two approach and which one is more cleaner and maintainable approach and if there is any efficiency benefit involved ?

I'd go with the first approach if:
The action is fired via different events. For example, you have an action that changes the language of the GUI from English to Arabic (where you need to re-arrange the components to lay from right to left) and that action can be fired via some key bindings like (Alt + R) and via a JMenuItem, and maybe via some buttons.
Several actions have the same base code. For example, a calculator application where each math operation button would fire the same action and based on the action command you can determine the operation from inside actionPerformd(). They share the GUI updates.
and I'd go with the second approach if:
The action is tied to only one event and you want to write it on the fly.
What I wouldn't do is something similar to this:
public class MainFrame extends JFrame implements ActionListener
but I would write:
public class CustomListener implements ActionListener
Also see:
Best practice for implementing ActionListener
best practice for gui and actionlistener
ActionListener best practices
Nested class vs implements ActionListener
Organize Code for GUI and ActionListener in Java
define button actions in inner class Vs define button actions in public class in swing
What is better to use: Action vs ActionListener?

Related

Should i use different instances of a single ActionListener for multiple buttons?

I have a single ActionListener for multiple buttons:
private class MyActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent event) {
if (event.getSource().equals(button1)) {
// do something
} else if (event.getSource().equals(button2)) {
// do something else
}
}
}
Which one of the following is the better way to add the ActionListener to these buttons and why?
1.:
button1.addActionListener(new MyActionListener());
button2.addActionListener(new MyActionListener());
2.:
MyActionListener mal = new MyActionListener();
button1.addActionListener(mal);
button2.addActionListener(mal);
I went with the 2nd option, but not sure it's the proper way.
This question asks for opinion and may be closed, and because of this, I'm answering as a community wiki, but I think the best answer is "it depends".
What I do is if all listeners are exactly the same, and you're not passing anything different into each constructor, then just have each button share the same listener. If I need to pass unique information into the listeners, then I use separate listeners, but really either will work, and neither will likely cause an appreciable difference in program operation or responsiveness.

Separating GUI from logic in Java

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.
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.
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());
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);
}
}
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.
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).
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());

How do I register an object's event listener to an event handler outside its class?

I have 2 classes, one JFrame class and one JButton class called LightsOutButton. The JFrame class has a collection of these LightsOutButton objects. I need to do actions when the individual buttons are pressed, but I can't access the JFrame class's methods from the event handler in my LightsOutButton class.
It seems like the best way to work around this would be to register each button with the JFrame class's event handler. How would I do that?
`// this is my button object. It has registered its action listener to itself(which I want to change)
public class LightsOutButton extends JButton implements ActionListener {
private boolean onOrOff; // true = on, false = off
private int iCoord;
private int jCoord;
public LightsOutButton() {
super.addActionListener(this);
onOrOff = false;
super.setContentAreaFilled(false);
}
}
// this is my JFrame class. This is the class that I want to handle each button object's events
public void actionPerformed(ActionEvent e) {
// Get the button that was clicked
JButton button = (JButton)e.getSource();
if(button == newGame) {
reset();
}
if(button == manual) {
if (manualMode == false) {
manualMode = true;
}
if (manualMode == true) {
manualMode = false;
}
}
// this is the implementation that I'm wishing for here:
if(button = LightsOutButton){
// do something
}
}`
Basically as I understand it, you have a group of LightsOutButton which have been added to a JFrame. In LightsOutButton you are trying to perform some action, but need to access the JFrame in some way to achieve it...
I would say you have the wrong concept. Sure, there's no reason why LightsOutButton can't have it's own event handlers to manage it's internal state, but beyond that, it shouldn't be concerned about anybody else.
It seems like the best way to work around this would be to register each button with the JFrame class's event handle
Would be an accurate assessment. How you do it will depend on how you've managed your code, but probably the simplest would be to add the LightsOutButtons to some kind of List or array and add and register the handler from within a loop.
Identification of each light will be your next problem, but you could use the reference from the List/array, or set the name property of each light or the actionCommand property depending on your needs.
If each light has a "particular" task to do, you could consider using a Action or specialised ActionListener for each one, but that comes down to needs
in your JFrame class, when you add you buttons
LightsOutButton b = new LightsButton(..);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//you have acces to JFrame methods ..
});

return a variable value to main on button click

I want that main should print hello (in a pop up dialogue box) everytime the button is clicked. So I designed the following program but it doesn't seem to work. The program compiles and executes just fine but when I click the button, I don't see the any dialogue box popping up. I have put in place the getter and setter of the boolean variable (button_clicked) whose value is being tested in the main() block.
Please help if you know..
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class abc extends JFrame implements ActionListener{
boolean button_clicked = false;
JButton b1;
abc(){
this.setSize (400, 400);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.createUI();
}
void createUI(){
this.setLayout(null);
b1 = new JButton("x");
b1.setSize(110,30);
b1.setLocation(10,210);
this.add(b1);
b1.addActionListener(this);
}
public boolean isButton_clicked() {
return button_clicked;
}
public void setButton_clicked(boolean button_clicked) {
this.button_clicked = button_clicked;
}
public void actionPerformed(ActionEvent arg0) {
button_clicked = true;
//I don't want to print the dialogue box from here..
//I want to keep everything under main()'s control.
}
}
public class tempMain extends JFrame {
public static void main(String[] args) {
abc temp = new abc();
temp.setVisible(true);
while(true){
if(temp.isButton_clicked())
JOptionPane.showMessageDialog(null, "Hello");
}
}
}
Move the JOptionPane.showMessageDialog() call under the actionPerformed() method and delete the while() thing under the main method.
As has already been pointed out by a number of people, you approach and design are flawed. I won't go into further as it has already been dealt with, but as a suggestion you could try...
abc temp = new abc();
temp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
JOptionPane.showMessageDialog(null, "Look Ma, I'm in Main...");
}
});
temp.setVisible(true);
And in your abc class...
class abc JFrame implements {
// build your class as normal
public void addActionListener(ActionListener listener) {
b1.addActionListener(listener);
}
}
You might want to read about "How to Write an Action Listener" to help you implement an ActionListner in your code.
The basic idea for your code would be to:
Declare an event handler class
Register an instance of the event handler class as a listener with your JButton
Include code that implements the methods in listener interface. In your case, you would over-ride actionPerformed() and write your logic over there to show a dialog box. "How to Make Dialogs" would be another useful tutorial for you.
As #Quinman pointed out, your code design is really flawed. I understand that you do not want to put the JOptionPane under the actionperformed method for some personal reason which I don't understand. Based on that premise, I think that the best solution would be to create a callback, that is, make your JFrame know the main class and call it when the button is clicked.
It is possible to make this mechanism independent of the main class. Please check the Observer design pattern in order to understand how to do that.
Your code also has another flaw - when the button is clicked I get infinite Hello messages. In order to avoid that, you should set the button_clicked variable to false. I only mention that as a general tip, for in truth you really should get rid of the busy wait that your while is causing.
You may be looking for a modeless dialog. In this example, the main panel, named Observer, listens to an ObservedPanel in a dialog. By using a PropertyChangeListener, any changes made to the dialog are immediately reflected in the main panel.
Judging from the comments you provided, you want to reuse your abc class (which has a very poor name and does not comply to the Java naming standards) for several different purposes, so you do not want to include the code which is executed when you press the button in the abc class.
There are multiple solutions for this (where the first is my preferred one)
Pass an Action in the constructor of your abc class and couple that Action to the JButton. This way the class which constructs the abc instance is responsible for the behavior when the button is pressed
make abc abstract and let your ActionListener call that abstract method. You can then make concrete implementations of this class each time you want different behavior.
Further notes on your code:
get rid of that while( true ) loop
get rid of the null layout and use a LayoutManager instead
Swing components should be created and accessed on the Event Dispatch Thread. Consult the Concurrency in Swing tutorial for more information

How can I place #Action methods in a separate class from a Swing component?

When developing Swing applications, I've typically defined a delegate interface for each UI component for action callbacks. For example, if there is a class, MyDialog, which contains a button, MyButton, then the ActionListener for MyButton will call MyDialog.Delegate.OnMyButtonClick(Event e). The UI component then becomes "dumb" and requires a controller to handle events as well as update the component itself.
I thought that by using the Swing Application Framework's #Actions, I could get around creating delegate interfaces and implementations by defining #Action methods in implementation classes, letting the ApplicationContext figure out what to call. Apparently, that is not the case, as I don't see any clear way of adding those classes into the ApplicationContext, nor do I see any examples out there of doing such a thing.
Has anyone managed to use SAF in this manner so that there is a clean separation between UI and UI action code?
I've discovered a good way to keep the UI separate from the behavior using #Actions.
First, create a UI Component, say a JPanel with a button and then give it a public method that can be used to set the action of the Button:
class CustomJPanel extends JPanel {
private JButton myButton;
public CustomJPanel() {
initializeComponents();
}
public void initializeComponents() {
myButton = new JButton();
}
public void setButtonAction(javax.swing.Action action)
{
myButton.setAction(action);
}
}
Next, create an Action class that will provide the logic for that button:
class CustomJPanelActions {
#Action
public void doSomething()
{
JOptionPane.showMessageDialog(null,"You pressed me!");
}
}
Finally, setup the application controller and during setup, assign the appropriate action to the appropriate UI:
class MyApp extends SingleFrameApplication {
private JFrame mainFrame;
private JLabel label;
#Override
protected void startup() {
getMainFrame().setTitle("BasicSingleFrameApp");
CustomJPanel panel = new CustomJPanel();
panel.setButtonAction(getContext().getActionMap(new CustomJPanelActions()).get("doSomething");
show(panel);
}
public static void main(String[] args) {
Application.launch(BasicFrameworkApp.class, args);
}
}
In this way, the UI is logically separated from the control (i.e. Action) and can be tested on its own. The controller can make any decisions it needs to in order to determine what Action set to use and which specific action to assign to the UI controls. That is, one can create a Test Action Set and a Live Action Set, etc.
This method of using SAF has worked rather well for me.
The SAF javadoc has some information on how to do this sort of thing in the doc for ActionManager#getActionMap

Categories