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();
}
});
Related
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).
I've been learning quite a lot in Java recently but something has been really bugging me. I learned / was taught how to use ActionListeners when the program involves a constuctor, for example,
public class test extends JFrame implements ActionListener {
JButton button;
public test
{
setLayout(null);
setSize(1920,1080);
setTitle("test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button = new JButton("");
button.setBounds(x,x,x,x);
button.AddActionListener(this); //What can replace the this parameter here.
button.setVisible(true);
add(button);
}
public static void main(String[] args) {
test testprogram = new test();
test.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent clickevent) {
if (clickevent.GetSource() == button) {
//DoSomething
}
}
It can be anything which implements ActionListener.
You might want to consider not making your JFrame implement ActionListener: this means that
It is part of the class' interface that it implements actionPerformed; but you probably don't want other classes to call that directly.
You can only implement it "once", so you end up having to have conditional logic to determine what the source of the event was, and then handle it appropriately.
The alternative is to create a button-specific action listener:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent clickevent) {
// Don't need to check if it is from button, nothing else
// could have created the event.
}
});
and remove implements ActionListener from the test class.
It is instance of class which is going to handle ActionEvent.
From the Documents
Register an instance of the event handler class as a listener on one
or more components. For example:
someComponent.addActionListener(instanceOfMyClass);
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 */);
}
}
I have class DnyMesice what creates many instances of JButton. Every instance contains variable poznamkaDne. This class DnyMesice contains actionListener to find poznamkaDne value of pushed JButton.
I have class Gui what creates one instance of mentioned class DnyMesice and one instance of JTextArea.
How can I refresh value of JTextArea (names poznamkovePole) if some JButton (in class DnyMesice) is pushed?
public class DnyMesice extends JPanel {
public String poznamkaDne="first note";
jButton tlacitkoDen;
public void zobrazMesic(Calendar kalendar){
for (c=1; c<30; c++){
tlacitkoDen = new JButton(Integer.toString(denvMesici));
tlacitkoDen.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent evt) {
poznamkaDne="New note";
};
});
add(tlacitkoDen);
}
}
}
public class Gui extends JFrame {
...
public void zobrazKalendar(){
...
panel3 = new JPanel();
panel3.setLayout(new FlowLayout());
add(panel3);
JTextArea poznamkovePole;
poznamkovePole = new JTextArea();
poznamkovePole.setColumns(30);
poznamkovePole.setRows(5);
poznamkovePole.setText(panel2.poznamkaDne);
panel3.add(poznamkovePole);
}
Now the program shows in JTextArea only "first note" (which is defined during creating of instance JButton) but hot to refresh it after ActionListener action?
Maybe better if you will use:
Add to DnyMesice JTextArea link and in ActionListener change text.
public class DnyMesice extends JPanel {
private JTextArea poznamkaDne;
jButton tlacitkoDen;
public DnyMesice (JTextArea jTextArea){
this.poznamkaDne = jTextArea;
}
public void zobrazMesic(Calendar kalendar){
for (c=1; c<30; c++){
tlacitkoDen = new JButton(Integer.toString(denvMesici));
tlacitkoDen.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent evt) {
poznamkaDne.setText("New note");
};
});
add(tlacitkoDen);
}
}
}
P.S. - And please don't forget to use Code Conventions for the Java
Modify as follows:
// add these methods
public void setPoznamkaDne(String s) {
poznamkaDne = s;
}
public String getPoznamkaDne() {
return poznamkaDne;
}
// CHANGE this method (KEEP the rest of the code!)
public void actionPerformed(ActionEvent evt) {
setPoznamkaDne("New note");
};
poznamkovePole.setText(panel2.getPoznamkaDne());
These changes should allow you to update the text. BUT you need to either call poznamekovePole.setText() somehow, or implement an advanced listener class. I recommend combining your class like #Too Strong Magic said, above.
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
}
}