Nested class vs implements ActionListener - java

Are there any benefits or drawbacks to creating a nested class that implements ActionListener:
public class Foo{
Foo(){
something.addActionListener(new ButtonListener());
}
//...
private class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
//...
}
}
}
versus implementing ActionListener in the main class itself:
public class Foo implements ActionListener{
Foo(){
something.addActionListener(this);
}
//...
public void actionPerformed(ActionEvent e){
//...
}
}
I've seen both examples quite often, and just want to know if there's a 'best practice.'

#Ankur, you can still use anonymous inner classes as your listeners and have a separate free-standing control class and thus have code that's quite maintainable, a technique I like to use a bit. For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnonymousInnerEg {
private static void createAndShowUI() {
GuiPanel guiPanel = new GuiPanel();
GuiControl guiControl = new GuiControl();
guiPanel.setGuiControl(guiControl);
JFrame frame = new JFrame("AnonymousInnerEg");
frame.getContentPane().add(guiPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class GuiPanel extends JPanel {
private GuiControl control;
public GuiPanel() {
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.startButtonActionPerformed(e);
}
}
});
JButton endButton = new JButton("End");
endButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.endButtonActionPerformed(e);
}
}
});
add(startButton);
add(endButton);
}
public void setGuiControl(GuiControl control) {
this.control = control;
}
}
class GuiControl {
public void startButtonActionPerformed(ActionEvent ae) {
System.out.println("start button pushed");
}
public void endButtonActionPerformed(ActionEvent ae) {
System.out.println("end button pushed");
}
}

I think first approach is better, as your class will have a separate code for handling action. And usually also composition is better than inheritance so a class should extend a class or implement a interface only if it truly is-a super type.
Also for maintainability, let us say Foo class has a new requirement to listen for another different type of events and then perform action, in that case also first class can be easily modified.
If I am not worried about maintainability I would rather go for a anonymous class.

If the class Foo has no other responsibility than encapsulating this button, then the first solution is sort of ok.
However, as soon as Foo gets more "somethings" that it has to listen to then it gets messy. I prefer the second solution since it is more explicit and has a better scalability.
An even better solution may be to create an anomymous inner class.
public class Foo{
Foo(){
something.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
//...
}
});
}
}

Usually you want to use a nested or even anonymous class rather than exposing ActionListener to the API of the enclosing class. (public class Foo implements ActionListener -> Javadoc will state that Foo is an ActionListener, though this is usually just an implementation detail -> bad)

Related

Rewriting the code so that its readability is improved without changing its behaviour

public class A2 {
public class B implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Fing");
}
}
public class C implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Fang");
}
}
public class D implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Foom");
}
}
public A2(){
JButton a = new JButton("Fing");
JButton b = new JButton("Fang");
JButton c = new JButton("Foom");
a.addActionListener(new B());
b.addActionListener(new C());
c.addActionListener(new D());
}
public static void main(String[] args) {
A2 a2 = new A2();
}
The problem I encountered is quite simple, but complex. I want it to shorten the code without retouching its functionality. For example, the code is showing to many actionlisteners and actionperformed, and I was trying to make it one class pulling out System.out.println(); and putting in String value on it. However, the coding does not work in this simple ways. Please help me out to curtail this code as simple and increase the readability. Thanks.
It's impossible to know what things you could do, I'm personally a fan of self documenting code, so sometimes, you need to be careful when trying to optimise solutions.
My first thought might be to start with the Action's API, which allows you to design a self contained unit of work
public class CommonAction extends AbstractAction {
public CommonAction(String name) {
putValue(NAME, name);
putValue(SHORT_DESCRIPTION, "This is a tool tip for " + name);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(getValue(NAME));
}
}
You could extend it further to provide more customisation if you needed, overriding the actionPerformed method, but, that's up to you.
Then you just need to apply to your buttons...
public class A2 {
public A2() {
JButton a = new JButton(new CommonAction("Fing"));
JButton b = new JButton(new CommonAction("Fang"));
JButton c = new JButton(new CommonAction("Foom"));
}
}
Or your menu's or your key bindings, Action is a rather flexible API supported by a number of other components
You can define single class MyActionListener which implements ActionListener as shown below:
public class MyActionListener implements ActionListener {
private String input;
public MyActionListener(String input) {
this.input = input;
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(input);
}
}
public A2(){
String[] inputs = {"Fing","Fang","Foom"};//Array of JButton inputs
for(int i=0;i<inputs.length;i++) {
JButton jButton = new JButton(inputs[i]);//create JButton instance
jButton.addActionListener(new MyActionListener(inputs[i]));
}
}

How to add a listener to a button from a ActionListener inner class?

I'm trying to do a simple calculator program by building my swing interface on netbeans.
I want to have 3 Classes:
GUI Class - which holds the codes for building the interface
Listener Class - holds all the listener in the GUI interface
Boot Class - this will start the application
For simplicity, I will post my code for a single button. My goal here is to change the Buttons visible text from "1" to "11" to test my design. After verifying that my design works I will continue on working on other buttons.
calculatorGUI.class
import javax.swing.JButton;
public class calculatorGUI extends javax.swing.JFrame {
public calculatorGUI() {
initComponents();
}
private void initComponents() {
oneBtn = new javax.swing.JButton();
oneBtn.setText("1");
}
private javax.swing.JButton oneBtn;
public JButton getOneBtn() {
return oneBtn;
}
public void setOneBtn(String name) {
oneBtn.setText(name);
}
}
Listener.class
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Listener {
class oneBtnListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ev) {
calculatorGUI g = new calculatorGUI();
g.setOneBtn("11");
}
}
}
Boot.class
public class Boot {
public static void main(String[] args) {
calculatorGUI gui = new calculatorGUI();
Listener listen = new Listener();
Listener.oneBtnListener oneListen = listen.new oneBtnListener();
gui.getOneBtn().addActionListener(oneListen);
gui.setVisible(true);
}
}
The problem is, nothing happens when I click the button. It seems that the actionListener is not being registered to the button. Can I ask for your help guys on which angle I missed?
The issue I am seeing is how you are initializing calculatorGUI twice, once with the default value and another with the changed value. Take out the initialization of calculatorGUI within your Listener class and pass it from your Boot class and it should work fine.
Although if I were you, I would add the GUI implementations within the GUI class, having it within the listener class that is using within the main function is not something I have seen before and would probably not advise.
Modify your code accordingly,
class Listener {
class oneBtnListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ev) {
if(ev.getActionCommand() == "1")
{
JButton btn = (JButton)ev.getSource();
btn.setText("11");
}
}
}
}
class calculatorGUI extends javax.swing.JFrame {
public calculatorGUI() {
initComponents();
}
private void initComponents() {
oneBtn = new javax.swing.JButton();
oneBtnListener btnListener = new Listener().new oneBtnListener();
oneBtn.setText("1");
oneBtn.setBounds(100,100,100,25);
oneBtn.addActionListener(btnListener);
add(oneBtn);
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
}
private javax.swing.JButton oneBtn;
public JButton getOneBtn() {
return oneBtn;
}
public void setOneBtn(String name) {
oneBtn.setText(name);
}
}
You can change now other part according to your requirement, I just
gave you "1" -> "11", but you can do more.
Best of Luck.

Running a "this" Method inside actionPerformed - JButton?

I have a JButton which I added a actionPerformed, and I tried to write a "this" method and it won't allow it. How can I do this? This is example of what I want to do:
public void methodName(String results) {
this.results = results;
}
Button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
this.methodName(asdf);
}
Because it's an anonymous class, using this will refer to the anonymous class instance, not your overall class. To get around this, denote that you want to reference your outer class specifically:
Something some = new Something() {
public void overridden() {
YourClass.this.methodName("test");
}
};
Your class in anonymous, so in anonymous context, this does not make any sense. What do you mean by this? If you mean the button, your answer is event.getSource()
In your code, this refers to your ActionListener when you call the method.
If you want to call methodName() from the enclosing class, you have two choices :
remove this:
Button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
methodName(asdf);
}
store a reference to the enclosing class and use it:
final MyClass enclosingClass = this;
Button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
enclosingClass.methodName(asdf);
}
You can not use "this" keyword inside inner class to access outer class method. if we use this then it will refer to the inner class.Instead of that just use the method name.see the example.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
public class TestButton
{
String results = "";
JButton Button = new JButton();
public TestButton(){
Button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
methodName("Test");
this.show();
}
public void show(){
System.out.println("hi");
}
});
}
public void methodName(String results)
{
this.results = results;
}
}

How to observe a JButton from a different class?

So I have a class that extends a JPanel and within the constructor I add my JButtons and whatever else I need to add. I also have a MainFrame class that is the container (JFrame) and this class will take an argument from a class called FrameSwitcher (Controller) which will assess what buttons were clicked, and pass the information to the MainFrame
I'm having troubles doing this, I can't find a proper way to do this. I do also wish to maintain the JButtons private and non static.
JPanel example:
public class MainMenu() {
private JButton btnSinglePlayer, btnMultiPlayer;
public MainMenu() {
setLayout(null);
btnSinglePlayer = new JButton("singlePlayer");
btnSinglePlayer.setBounds(320, 25, 275, 130);
add(btnSinglePlayer);
btnMultiPlayer = new JButton("MultiPlayer");
btnMultiPlayer.setBounds(320, 170 , 275, 130);
add(btnMultiPlayer);
}
}
FrameSwitcher:
public class FrameSwitcher implements panelListener { // panelListener is an interface defined else where.
public FrameSwitcher(MainFrame frame) {
// This is irrelevant to the question.
}
#Override
public void gamePanel() {
System.out.println("GamePanel Event: Recieved");
}
#Override
public void mainMenu() {
System.out.println("mainMenu Event: Recieved");
}
#Override
public void scoreBoardPanel() {
System.out.println("scoreBoardPanel Event: Recieved");
}
}
Then my MainFrame:
public class MainFrame extends JFrame implements ActionListener {
private PanelListener panelListener;
private JFrame mainContainer = new JFrame("Game");
private JPanel mainMenu = new MainMenu();
public void start() {
mainContainer(mainMenu);
}
public MainFrame(JPanel frame) {
mainContainer.getContentPane().add(frame);
mainContainer.pack();
// Other methods to initialize the frame
return mainContainer;
}
public void switchFrames(PanelListener panelListener) {
this.panelListener = panelListener; // PanelListener is an interface.
}
public void actionPerformed(ActionEvent e) {
JButton source = (JButton)e.getsource();
if(source == MainMenu.btnSinglePlayer) {
if(panelListener != null) {
System.out.println("Recieved the event approriately.");
}
}
}
}
In this example, it does compile, but doesn't do what it is supposed to. Another thing is I currently have the JButtons as public and static, I don't want that.
In your MainMenu class, you need to add some kind of listener that interested parties can register with, so when some event occurs, they can be notified.
The simplest solution would be to provide a addActionListener method which delegated to each of the buttons. This, however, has may expose portions of the application you don't exposed (a listener now has direct access to the JButton and can do all kinds of nasty things to it).
A better solution would be to create something like a MainMenuListener which had methods like startSinglePlayer and startMultiPlayer
You would then provide a add/removeMainMenuListener method within in your MainMenu class.
Each button would then register there own actionListener and fire the appropriate menu listener event
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class MainMenu extends JPanel {
private JButton btnSinglePlayer, btnMultiPlayer;
public MainMenu() {
setLayout(null);
btnSinglePlayer = new JButton("singlePlayer");
btnSinglePlayer.setBounds(320, 25, 275, 130);
add(btnSinglePlayer);
btnSinglePlayer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireStartSinglePlayer();
}
});
btnMultiPlayer = new JButton("MultiPlayer");
btnMultiPlayer.setBounds(320, 170, 275, 130);
add(btnMultiPlayer);
btnMultiPlayer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireStartMultiPlayer();
}
});
}
public void addMainMenuListener(MainMenuListener listener) {
listenerList.add(MainMenuListener.class, listener);
}
public void removeMainMenuListener(MainMenuListener listener) {
listenerList.remove(MainMenuListener.class, listener);
}
public void fireStartSinglePlayer() {
MainMenuListener[] listeners = listenerList.getListeners(MainMenuListener.class);
if (listeners != null && listeners.length > 0) {
for (MainMenuListener listener : listeners) {
listener.startSinglePlayer();
}
}
}
public void fireStartMultiPlayer() {
MainMenuListener[] listeners = listenerList.getListeners(MainMenuListener.class);
if (listeners != null && listeners.length > 0) {
for (MainMenuListener listener : listeners) {
listener.startMultiPlayer();
}
}
}
public interface MainMenuListener extends EventListener {
public void startSinglePlayer();
public void startMultiPlayer();
}
}
First you have to create an ActionListener like this
class MyActionListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
//do your stuff
}
}
Then call,
yourButton.addActionListener(new MyActionListener());

A simple question regarding ActionListener and 'enter' key

I am working on an assignment, and I need to enter an SQL Query in a textfield. The user can either press the custom 'execute query' button, or they can press the enter key. When either of these are used, it is to trigger an ActionListener (no other listener is allowed). Is it as simple as writing:
if (e.getSource()=='querybutton' || e.getSource=='enter')
Or is there more to it than this?
As I said, it is a simple question (I know).
edit:
I would write this bit in my ActionPerformed as:
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==gui.executeQueryButton || e.getSource()==gui.enter)
{
String query = gui.queryText.getText();
//more code to follow
}
}
e.getSource() actually returns the object responsible for firing the event (not the name of the variable you used when creating the control). In this case, your button. You could in principle compare e.getSource() with the actual button instances. However, are you actually adding this action listener to buttons other than those two? Presumably you'd only have to add this listener to the two buttons for which you want this behavior -- in which case you wouldn't have to have this if check.
" Is it as simple as writing:
if (e.getSource()=='querybutton' || e.getSource=='enter')"
It's not simple to write this, but rather it is wrong to write it.
For one you don't want to compare Strings with ==, for another, you don't declare Strings with single quotes, and for a third, the enter key is not obtained in this way, but rather by adding the appropriate ActionListener object to the JTextField itself, and finally there should be in a single ActionListener class that handles this action, so the if block is completely unnecessary. This can probably be best done with a small inner private ActionListener class. You'd then create one object of this class and add it as an ActionListener for the querybutton and for the JTextField.
edit 1:
A more complete example of what I mean is shown below, a demo class that has a private inner handler class:
import java.awt.event.*;
import javax.swing.*;
public class ActionListenerEg extends JPanel {
private JButton queryButton = new JButton("Query");
private JTextField textField = new JTextField("hello", 20);
public ActionListenerEg() {
QueryListener qListener = new QueryListener();
queryButton.addActionListener(qListener);
textField.addActionListener(qListener);
add(queryButton);
add(textField);
}
private class QueryListener implements ActionListener {
public void actionPerformed(ActionEvent arg0) {
String textInField = textField.getText();
System.out.println("Use text in field, \"" + textInField + "\" to call SQL query in a background SwingWorker thread.");
}
}
private static void createAndShowUI() {
JFrame frame = new JFrame("ActionListenerEg");
frame.getContentPane().add(new ActionListenerEg());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
The ActionListener is fired either by pressing the button or by pressing enter from within the JTextField. I'd then have in my control class, code that is called inside of the actinoPerformed method.
edit 2: Having most handler or "control" code in its own Handler or Control class can be a good idea, but it doesn't have to implement ActionListener interface itself, but rather just have the code that will be called from within the ActionListener codes. For example, here I try to put all the handler code in its own class. It will have different methods that are called for various situations. e.g.,
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
public class ActionListenerEg extends JPanel {
private ActionListenerHandler handler;
private JButton queryButton = new JButton("Query");
private JButton displayButton = new JButton("Display");
private JTextField textField = new JTextField("hello", 20);
// pass in handler or handler
public ActionListenerEg(final ActionListenerHandler handler) {
this.handler = handler;
QueryListener qListener = new QueryListener();
queryButton.addActionListener(qListener);
textField.addActionListener(qListener);
displayButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (handler != null) {
handler.displayActionPerformed(e);
}
}
});
add(queryButton);
add(textField);
add(displayButton);
}
private class QueryListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (handler != null) {
String textInField = textField.getText();
handler.doQueryAction(e, textInField);
}
}
}
private static void createAndShowUI() {
ActionListenerHandler handler = new ActionListenerHandler();
JFrame frame = new JFrame("ActionListenerEg");
frame.getContentPane().add(new ActionListenerEg(handler));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class ActionListenerHandler {
public void displayActionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog((Component) e.getSource(), "Display things!");
}
public void doQueryAction(ActionEvent e, String textInField) {
String text = "We will use \"" + textInField + "\" to help create and run the SQL Query";
JOptionPane.showMessageDialog((Component) e.getSource(), text);
}
}
Please ask questions if it's clear as mudd, or if anything is wrong.

Categories