ActionListener in parent class - java

I am struggling with the ActionListener in Java in a parent class, I tried a bunch of possible solutions but could not get it work. This here also did not help:
Java actionlistener actionPerformed in different class
The problem is as follows:
Class2 extends Class1, I have a button in Class2. As soon as the button in Class2 is pressed, Class1 should be notified through action listener and perform the event.
I'm struggling to let Class1 know that the event has happened. It looked pretty simple to me, but nevertheless I'm struggling.
Your help will be much apprechiated, thank you!
Parent Class
package test;
//imports removed for better visibility
public class ParentClass extends JPanel implements ActionListener{
JFrame frame;
public void createParentGui() {
frame = new JFrame("Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel mainCard = new JPanel(new CardLayout(20, 20));
ChildClass card1 = new ChildClass();
mainCard.add(card1);
frame.add(mainCard, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Button pressed, action!");
}
}
Child Class
package test;
//imports removed for better visibility
public class ChildClass extends ParentClass {
ActionListener listener = null; //this is probably not right, how to do
//with a local variable when passing it to the parent class?
public Child() {
createGui();
}
private void createGui() {
final JButton b = new JButton("press me");
b.addActionListener(listener);
add(b);
}
}

ChildClass has all of the fields and methods that ParentClass does (in addition to its own unique fields and methods). This is how inheritance works.
So, since ParentClass is an ActionListener, that means that ChildClass is too. More specifically, ChildClass has inherited the public void actionPerformed(ActionEvent e) method of ParentClass.
Therefore, change b.addActionListener(listener); to b.addActionListener(this). (you can also remove the listener field of ChildClass)
The new code will pass "this" ChildClass object to b, which will then call actionPerformed(ActionEvent e) whenever the button is pressed. And since any ChildClass object has the actionPerformed(ActionEvent e) of ParentClass, that means that ParentClass#actionPerformed(ActionEvent) will be called (as you intended).

Related

use controller in actionPerformed method - needs to be final?

I'm playing around with the MVC pattern and I am stuck.
My simple application (where you can roll dice) has a MainView class that creates and holds a few other Views like ButtonPanelView (which has buttons..). MainView is created by my MainController. MainController also has my rollDice method which will call the diceModel class and change the value.
Now, my rollDice Button is in die ButtonPanelView and thats where the ActionListener is, too. I'm trying to call controller.rollDice() from within the actionPerformed method, but the error says "Variable mainController is accessed from within inner class, needs to be declared final".
I don't know how to do that, because my this.mainController can't be final, since it is in the Constructor. Here's my code:
public class ButtonPanelView extends JPanel{
private MainController mainController;
private JButton rollDiceBtn = new JButton("roll dice");
private JPanel pan = new JPanel();
public ButtonPanelView(MainController mainController){
this.mainController = mainController;
add(pan);
pan.add(rollDiceBtn);
rollDiceBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
mainController.rollDice();
}
});
}
}
Thanks for the help :)
You can resolve the compilation error by using the OuterClass.this notation for referencing mainController within the anonymous ActionListener class you create in the constructor:
rollDiceBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
ButtonPanelView.this.mainController.rollDice();
}
});

How to access an object within another class (Java)

Here's an abrevated version of what my code looks like:
public class ColorFactory extends JFrame {
public ColorFactory(){
buildTopPanel();
}
public void buildTopPanel(){
JPanel topPanel = new JPanel();
this.add(topPanel, BorderLayout.NORTH);
}
}
As you can see I have a method that makes a new JPanel object when called. How can I access that particular JPanel object from another class? I have a button listener class that I want to change the color of the JPanel from outside the ColorFactory class. This code is right after the ColorFactory class.
public class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Change JPanel color here.
}
}
Would it be better just to instantiate JPanel in the ColorFactory constructor and then just access it through there?
For starters, you need to make the JPanel a field in ColorFactory, so references to it don't disappear when you exit buildTopPanel(). Once you've saved a reference to it, then you have a couple of choices. From the design standpoint, the bad choice is to expose it, e.g.:
JPanel getTopPanel(){
return topPanel;
}
The better choice is to have your action listener send a message to ColorFactory that says "respondToButton(Color newColor)", and have ColorFactory decide to change topPanel's color... e.g.:
public void respondToButton(Color newColor){
topPanel.setBackground(newColor);
}
You are facing a design issue; in general, this type of situations require more investigation to understand how to end up with a clean and maintainable design.
However, For the specific problem you are reporting, I would:
Create a constructor of ButtonListener that receives a parameter (i.e. the ColorFactory) which could access the information you need, so that you can initialize a field in ButtonListener itself
Create a method changeColor in the ColorFactory. This method actually applies the color change
In the ButtonListener, invoke changeColor on the field, i.e. the reference to the ColorFactory
You should make the JPanel a field of the class like this:
public class ColorFactory extends JFrame {
JPanel topPanel;
public ColorFactory(){
buildTopPanel();
}
public void buildTopPanel(){
topPanel = new JPanel();
this.add(topPanel, BorderLayout.NORTH);
}
public void changeColor(Color color) {
//color changing code here
}
}
now You can get the JPanel from another class.
All you have to do now, is get the ColorFactory into your Button listener:
public class ButtonListener implements ActionListener{
ColorFactory colorFactory;
public ButtonListener(ColorFactory colorFactory) {
this.colorFactory = colorFactory;
}
public void actionPerformed(ActionEvent e) {
colorFactory.changeColor(/* color here */);
}
}

Error when extending class

I'm pretty new to Java, so I don't know what's going wrong. What I have here is a code that is, when it's executed, supposed to "open" a window once, but whenever I extend the class ColoredWordsExperiment in the ButtonHandler class, the window opens infinitely really quickly which nearly causes my computer to crash everytime. If I leave out the extension then it works fine, but then I won't be able to use the objects from the ColoredWordsExperiment class in the ButtonHandler class... Below you can find the code (I left some unimportant stuff out otherwise it'd become too long).
public class ColoredWordsExperiment {
JFrame frame;
ButtonHandler buttonHandler;
ColoredWordsExperiment(){
frame = new JFrame("Colored Words Experiment");
frame.setSize(1200, 150);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button1 = new JButton("Matching");
label1 = new JLabel("test");
label1.setPreferredSize(new Dimension(90, 40));
JPanel labelContainer = new JPanel();
labelContainer.add(label1);
JPanel buttonContainer = new JPanel();
buttonContainer.add(button1);
frame.add(buttonContainer, BorderLayout.SOUTH);
frame.add(labelContainer, BorderLayout.NORTH);
buttonHandler = new ButtonHandler();
button1.addActionListener(buttonHandler);
}
public static void main(String[] arg) {
new ColoredWordsExperiment();
}
}
-
public class ButtonHandler extends ColoredWordsExperiment implements ActionListener {
#Override
public void actionPerformed(ActionEvent e){
if (e.getActionCommand().equals("Matching")) {
System.out.println("Matching");
label1.setText("Text changed");
}
}
}
There's no reason to extend ColoredWordsExperiment in this case. You should simply be implementing ActionListener.
You're essentially initializing another instance of ColoredWordsExperiment inside itself with a single additional method. This causes your constructor be called again, which causes the GUI window to be recreated.
See Inheritance for more details.
If you want to change a field in ColoredWordsExperiment from your ActionListener implementation, you'll want to pass a reference during construction.
public class ButtonHandler implements ActionListener {
ColoredWordsExperiment coloredWords;
public ButtonHandler(ColoredWordsExperiment coloredWords) {
this.coloredWords = coloredWords;
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Matching")) {
System.out.println("Matching");
coloredWords.label1.setText("Text changed");
}
}
}
In this case, you also have the option of creating an anonymous inner class. With this technique, you can completely get rid of the ButtonHandler class.
Inside ColoredWordsExperiment instead of
buttonHandler = new ButtonHandler();
button1.addActionListener(buttonHandler);
You can use
button1.addActionListener(new ActionListener() {
if (e.getActionCommand().equals("Matching")) {
System.out.println("Matching");
label1.setText("Text changed");
}
});
See Anonymous Classes
In the constructor of the parent class, you are creating one of those ButtonHandlers wich, in turn, runs the constructor code again.
You should not instantiate this class in the constructor (or use a different name for it if you are attempting to use one with the same name)
Okay i can see what tour problem is. Now i don't know why you want to extend it, I'm just trying to answer your question (b/c there is no need to extend an action listener to your main class). when you define the class method you didn't put a public, and then when you put under the main method to run it, it could be confused and make infinite frames. You should change it to this:
public class ColoredWordsExperiment {
JFrame frame;
ButtonHandler buttonHandler;
public ColoredWordsExperiment(){//add the public!
frame = new JFrame("Colored Words Experiment");
frame.setSize(1200, 150);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button1 = new JButton("Matching");
label1 = new JLabel("test");
label1.setPreferredSize(new Dimension(90, 40));
JPanel labelContainer = new JPanel();
labelContainer.add(label1);
JPanel buttonContainer = new JPanel();
buttonContainer.add(button1);
frame.add(buttonContainer, BorderLayout.SOUTH);
frame.add(labelContainer, BorderLayout.NORTH);
buttonHandler = new ButtonHandler();
button1.addActionListener(buttonHandler);
}
public static void main(String[] arg) {
new ColoredWordsExperiment();
}
}
Additionally, it's not a good idea to define your buttonHandler variable as your ButtonHandler class in your Main class but then have your ButtonHandler class extend off your Main class. that could cause a loop. you should either not extend your second class or define your buttonHandler variable in a different way.
It sounds like you need to read up and understand the difference between classes and objects. To help you with that I want to illustrate a problem with the way you were originally thinking about your code with a simplified example:
class A {
int x;
B b = new B();
}
class B extends A {
}
class Main {
public static void main(String args[]) {
A a = new A();
a.x = 42;
a.b.x = 53;
System.out.println(a.x);
System.out.println(a.b.x);
}
}
As expected, the object a in main() has a field named x and we can set it to 42. However, the object b inside of a has its own field named x which is completely unrelated to the field x inside of the object a.
If you can get your head around the concept of objects, you will be well on your way to becoming a good Java programmer.

notify the caller class something has happened

Say, you have a subclass of JFrame, and use it to create your own custom JFrame. In this class (we'll call it mainFrame), we create a reference to another custom JFrame class (we'll call this one sidePanel).
In sidePanel, you have different buttons, radio buttons,..
My question is, is there a way to notify mainFrame the user presses on a button?
I've created a (untested) example of what I mean:
class mainFrame extends JFrame {
public mainFrame() {
super("main frame");
//...........
sidePanel panel = new sidePanel();
//...........
}
public static void main(String[] args) {
mainFrame mainF = new mainFrame();
//.........
}
}
And the sidePanel class:
class sidePanel extends JFrame {
public sidePanel() {
super("sidePanel frame");
//...........
JButton button1 = new JButton();
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Notify mainFrame somehow button is pressed
}});
//...........
}
}
To notify mainFrame of an event, the SidePanel instance (really bad name for a Frame) must have a reference to mainFrame. Pass mainFrame as an argument of the SidePanel constructor, and callback mainFrame from the actionPerformed method in SidePanel:
SidePanel panel = new SidePanel(this);
and in SidePanel:
public void actionPerformed(ActionEvent e) {
mainFrame.buttonHasBeenClicked();
...
}
This tightly couples both classes though. A way to decouple them is to make the SidePanel object accept listeners for custom events, and to fire such an event when the button is clicked. The mainFrame would construct the SidePanel instance, and add itself (or an inner anonymous class instance) as a listener to the sidePanel.
See this page for an example.

Swing - calling events from inside panel

I have simple Swing GUI with main window JFrame and its main panel derive from JPanel. The panel has some buttons that can be clicked and generate events.
I want these events affect data stored in JFrame because it is my main application - it has some queues for thread, open streams and so on.
So how do I make my button in panel invoke callbacks in its parent frame? What is best practice of this for Java/Swing?
To invoke methods in the parent frame you need a reference to the parent frame. So your JPanel's constructor can be declared like this:
public MyPanel(MyFrame frame){
super();
this.frame = frame;
//the rest of your code
}
And in the JFrame you invoke this constructor like this:
panel = new MyPanel(this);//this refers to your JFrame
In the event handlers attached to your buttons you now have access to the frame and can invoke the various methods as needed.
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//do some stuff
frame.someMethod();//invoke method on frame
//do more stuff
}
});
Have a look on this tutorial for using SwingWorker.
Use addActionListener method on desired buttons specifying the class implementing ActionListener.
ActionListenerClass actionListenerObject = new actionListenerClass();
JButton b = new JButton("Button");
b.addActionListener(actionListenerObject);
public class ActionListenerClass implements ActionListener(){
//or better : actionListenerClass extends AbstractAction
public void actionPerformed(ActionEvent e) {
}
}
EDIT:
Yes, I know this. But the action
listener I want to be in parent JFrame
class - this is the problem
then extends JFrame class making the new derived class implementing the desired interface.
You can implement the ActionListener in your class that has the JFrame (or extends it):
class MyPanelClass {
public MyPanelClass(ActionListener al)
{
//...
JButton myButton = new JButton("Button");
myButton.addActionListener(al);
//...
}
}
class MainClass extends JFrame implements ActionListener {
public void someMethod() {
MyPanelClass mpc = new MyPanelClass(this);
}
#Override
public void ActionPerformed(ActionEvent ev) {
// your implementation
}
}

Categories