0

Is there any way to move actionListener classes to stand-alone classes?

I made an exaple using Java and MVC design pattern. I have 3 buttons that change background color.

Here's Model

public class ChangeFontColorApplicationModel {
    public ChangeFontColorApplicationModel(){}
}

View

import java.awt.event.*;
import javax.swing.*;

public class ChangeFontColorApplicationView extends JFrame{
private JButton changeToYellowButton = new JButton("Yellow font");
private JButton changeToBlackButton = new JButton("Black font");
private JButton changeToBlueButton = new JButton("Blue font");

public ChangeFontColorApplicationView(){
    super("Change font");
    JPanel buttonPanel = new JPanel();

    buttonPanel.add(changeToYellowButton);
    buttonPanel.add(changeToBlackButton);
    buttonPanel.add(changeToBlueButton);

    this.add(buttonPanel);
}

public void addButtonActionListener(){
    changeToYellowButton.addActionListener(new yellowBackgroundHandler());
    changeToBlackButton.addActionListener(new yellowBackgroundHandler());
    changeToBlueButton.addActionListener(new yellowBackgroundHandler());
}
}

Controller

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ChangeFontColorApplicationController {
private ChangeFontColorApplicationView changeFontColorApplicationViewObject;
backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler();
backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler();
backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();

public ChangeFontColorApplicationController(ChangeFontColorApplicationView changeFontColorApplicationViewObject){
    this.changeFontColorApplicationViewObject = changeFontColorApplicationViewObject;

    yellowBackgroundHandlerObject.setNextHandlerInChain(blackBackgroundHandlerObject);
    blackBackgroundHandlerObject.setNextHandlerInChain(blueBackgroundHandlerObject);

    this.changeFontColorApplicationViewObject.addButtonActionListener();
}
}

Now I created an interface that handles the button events

public interface backgroundHandler extends ActionListener{
public void setNextHandlerInChain(backgroundHandler nextInChain);
public void handleCommand(String getActionCommandString);
public void actionPerformed(ActionEvent event);
}

Every class that implements the interfaces, handles the ActionEvent. If it cannot, it passes to the next class (as Chain of responsibility)

yellow font handler

import java.awt.Color;
import java.awt.event.ActionEvent;

public class yellowBackgroundHandler implements backgroundHandler{
private backgroundHandler nextInChain;
private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();

public yellowBackgroundHandler(){}

@Override
public void setNextHandlerInChain(backgroundHandler nextInChain) {
    this.nextInChain = nextInChain;
}

@Override
public void handleCommand(String getActionCommandString) {
    if(getActionCommandString.equalsIgnoreCase("Yellow font")){
        ChangeFontColorApplicationViewObject.setBackground(Color.yellow);
    }
    else {
        nextInChain.handleCommand(getActionCommandString);
    }
}

@Override
public void actionPerformed(ActionEvent event) {
    try{
        handleCommand(event.getActionCommand());
    }
    catch (Exception exeption){
        ChangeFontColorApplicationViewObject.setTitle(exeption.toString());            
    }
}
}

black font handler

import java.awt.Color;
import java.awt.event.ActionEvent;

public class blackBackgroundHandler implements backgroundHandler{
private backgroundHandler nextInChain;
private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();

public blackBackgroundHandler(){}

@Override
public void setNextHandlerInChain(backgroundHandler nextInChain) {
    this.nextInChain = nextInChain;
}

@Override
public void handleCommand(String getActionCommandString) {
    if(getActionCommandString.equalsIgnoreCase("Black font")){
        ChangeFontColorApplicationViewObject.setBackground(Color.BLACK);
    }
    else {
        nextInChain.handleCommand(getActionCommandString);
    }
}

@Override
public void actionPerformed(ActionEvent event) {
    try{
        handleCommand(event.getActionCommand());
    }
    catch (Exception exeption){
        ChangeFontColorApplicationViewObject.setTitle(exeption.toString());            
    }
}
}

blue font handler

import java.awt.Color;
import java.awt.event.ActionEvent;

public class blueBackgroundHandler implements backgroundHandler{
private backgroundHandler nextInChain;
private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();

public blueBackgroundHandler(){}

@Override
public void setNextHandlerInChain(backgroundHandler nextInChain) {
    this.nextInChain = nextInChain;
}

@Override
public void handleCommand(String getActionCommandString) {
    if(getActionCommandString.equalsIgnoreCase("Μπλε φόντο")){
        ChangeFontColorApplicationViewObject.setBackground(Color.BLUE);
    }
    else {
        ChangeFontColorApplicationViewObject.setTitle("This is the back end of COR");
    }
}

@Override
public void actionPerformed(ActionEvent event) {
    try{
        handleCommand(event.getActionCommand());
    }
    catch (Exception exeption){
        ChangeFontColorApplicationViewObject.setTitle(exeption.toString());            
    }
}
}

Finally I create the class with the main method

public class ChangeFontColorApp {
public static void main(String[] args){
    ChangeFontColorApplicationView changeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
    ChangeFontColorApplicationController changeFontColorApplicationControllerObject = new ChangeFontColorApplicationController(changeFontColorApplicationViewObject);

    changeFontColorApplicationViewObject.setVisible(true);
    changeFontColorApplicationViewObject.setSize(600, 600);
       changeFontColorApplicationViewObject.setDefaultCloseOperation(ChangeFontColorApplicationView.EXIT_ON_CLOSE);
}
}

Is there any way to make this work? I don't want to have an inner class for event handling with a multiple if blick in it. Any suggestions would be really appreciated.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Vassilis De
  • 363
  • 1
  • 3
  • 21
  • 1
    Please read [How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve). Walls of code are bad. No one wants to read all that. – Scott Solmer Jun 20 '14 at 12:13
  • Yes, you can write an action controller class. Your controller doesn't have to be one class. It can be many classes, each one responding to a certain action. Take a look at my [2048 Game in Java Swing](http://java-articles.info/articles/?p=516) article to see one way to use MVC to create a Java Swing application. – Gilbert Le Blanc Jun 20 '14 at 12:47
  • Ok guys I will read both two. Thank you for your suggestions – Vassilis De Jun 20 '14 at 13:42

1 Answers1

3

That won't work. But, the problem is not because you have standalone classes (using standalone classes is fine). Rather, the problem is:

In view class, you have:

public void addButtonActionListener(){
    changeToYellowButton.addActionListener(new yellowBackgroundHandler());
    changeToBlackButton.addActionListener(new yellowBackgroundHandler());
    changeToBlueButton.addActionListener(new yellowBackgroundHandler());
}

this means that clicks of all buttons are handled by yellowBackgroundHandler. But, yellowBackgroundHandler only accepts clicks from changeToYellowButton. In addition to that, yellowBackgroundHandler is not configured with next handler.

To fix this, in view class, change this:

public void addButtonActionListener(){
    changeToYellowButton.addActionListener(new yellowBackgroundHandler());
    changeToBlackButton.addActionListener(new yellowBackgroundHandler());
    changeToBlueButton.addActionListener(new yellowBackgroundHandler());
}

to

public void addButtonActionListener(backgroundHandler handler){
    changeToYellowButton.addActionListener(handler);
    changeToBlackButton.addActionListener(handler);
    changeToBlueButton.addActionListener(handler);
}

and in controller class, change this:

this.changeFontColorApplicationViewObject.addButtonActionListener();

to

this.changeFontColorApplicationViewObject.addButtonActionListener(yellowBackgroundHandlerObject);

Another problem that I see is that yellowBackgroundHandler, blackBackgroundHandler, and blueBackgroundHandler have private variable ChangeFontColorApplicationViewObject, which points to its own instance of ChangeFontColorApplicationView. We need this variable to point to the instance created in the main method. To do this, change this:

private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
public blueBackgroundHandler(){}

to:

private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject;
public blueBackgroundHandler(ChangeFontColorApplicationView ChangeFontColorApplicationViewObject){
  this.ChangeFontColorApplicationViewObject = ChangeFontColorApplicationViewObject;
}

and change this:

private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
public blackBackgroundHandler(){}

to:

private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject;
public blackBackgroundHandler(ChangeFontColorApplicationView ChangeFontColorApplicationViewObject){
  this.ChangeFontColorApplicationViewObject = ChangeFontColorApplicationViewObject;
}

and change this:

private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
public blueBackgroundHandler(){}

to:

private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject;
public blueBackgroundHandler(ChangeFontColorApplicationView ChangeFontColorApplicationViewObject){
  this.ChangeFontColorApplicationViewObject = ChangeFontColorApplicationViewObject;
}

finally, change this:

backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler();
backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler();
backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();

public ChangeFontColorApplicationController(ChangeFontColorApplicationView changeFontColorApplicationViewObject){
    this.changeFontColorApplicationViewObject = changeFontColorApplicationViewObject;

    yellowBackgroundHandlerObject.setNextHandlerInChain(blackBackgroundHandlerObject);
    blackBackgroundHandlerObject.setNextHandlerInChain(blueBackgroundHandlerObject);

    this.changeFontColorApplicationViewObject.addButtonActionListener();
}

to

backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler();
backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler();
backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();

public ChangeFontColorApplicationController(ChangeFontColorApplicationView changeFontColorApplicationViewObject){
    this.changeFontColorApplicationViewObject = changeFontColorApplicationViewObject;
    this.yellowBackgroundHandlerObject = new yellowBackgroundHandler(this.changeFontColorApplicationViewObject);
    this.blackBackgroundHandlerObject = new blackBackgroundHandler(this.changeFontColorApplicationViewObject);
    this.blueBackgroundHandlerObject = new blueBackgroundHandler(this.changeFontColorApplicationViewObject);

    yellowBackgroundHandlerObject.setNextHandlerInChain(blackBackgroundHandlerObject);
    blackBackgroundHandlerObject.setNextHandlerInChain(blueBackgroundHandlerObject);

    this.changeFontColorApplicationViewObject.addButtonActionListener(this.changeFontColorApplicationViewObject);
}
fajarkoe
  • 1,543
  • 10
  • 12
  • I'm afraid that didn't work either...would it be because of adding actionListeners in controller and not in view, before adding the buttons to buttonPanel? – Vassilis De Jun 20 '14 at 13:49
  • Perhaps you can elaborate more on "did not work either"? Does the yellow button work? – fajarkoe Jun 20 '14 at 14:00
  • No it does not... To be more specific, I found out that the event stucks into a while loop in EventDispatchThread.java, pumpEventsForFilter method. After being in the loop, debug just stays there. I cannot step over any further. It's like it cannot get out of while loop and stops debugging. What might be that? This is the same for every button. – Vassilis De Jun 20 '14 at 14:23
  • Ifound another problem. I have just updated the answer. Let me know how it goes. – fajarkoe Jun 20 '14 at 14:34
  • In the last expression `this.changeFontColorApplicationViewObject.addButtonActionListener(this.changeFontColorApplicationViewObject);` The addButtonActionListener() method takes a backgroundHandler argument. How am I supposed to cast that? And something else: The statements `backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler(); backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler(); backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();` shouldn't be different? Their contstructor takes an argument now. – Vassilis De Jun 20 '14 at 16:55
  • Sorry the last line should be `this.changeFontColorApplicationViewObject.addButtonActionListener(this.yellowBackgroundHandlerObject)`; – fajarkoe Jun 21 '14 at 03:04
  • I tried to run it the way you said, but it throwed exceptions in main because of calling an empty constructor in `backgroundHandler [color]BackgroundHandlerObject = new [color]BackgroundHandler();`statements. So I made some changes to the controller in order to run properly `backgroundHandler yellowBackgroundHandlerObject;//...` and inside constructor `this.yellowBackgroundHandlerObject = new yellowBackgroundHandler(this.changeFontColorApplicationViewObject);//and so for blue and black background handler}` The app runs,but still the buttons don't work.. – Vassilis De Jun 21 '14 at 09:22
  • I found the problem. I just had to set a Layout to container. Everything works now. Thank you very much for your help – Vassilis De Jun 25 '14 at 21:04