I am writing a setonclick listner, and I want to be able to refer to that button so that I can change its properties. I.e. make it disabled?
I get thismessage:
Cannot refer to a non-final variable confirmButton inside an inner class defined in a different method
confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
confirmButton.setEnabled(false);
}
});
This because you are probably trying to access that button from an anonymous class that you use in this way:
button.addActionListener(
new MyListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//do your things on button }
}
}
);
This doesn't work because in Java anonymous classes cannot see variables declared in methods in which they are declared too since their scope are separated. The only way to let your class see it is forcing the final constraint which assures the compiler that the variable won't change after being initialized, allowing it to extend its scope to the anonymous classes.
To quickly fix this you can access the button from the ActionEvent inside the actionPerformed:
((JButton)e.getSource()).setEnabled(false)
Otherwise you have to concretely declare your ActionListener somewhere or declare the buttons outside the method with static or final attribute.. especially if you plan to modify some elements by an action that is fired by another element.
I would recommend against the getSource; the documentation doesn't promise that it will be your button. You can either make your button final in the scope, or use a more sophisticated class
public class ComponentRelevantOnClickListener implements View.OnClickListener {
private JComponent component;
public ComponentRelevantOnClickListener(JComponent component) {
this.component = component;
}
}
// then, in your code...
confirmButton.setOnClickListener(new ComponentRelevantOnClickListener(confirmButton) {
public void onClick(View view) {
component.setEnabled(false);
}
});
If you move toward a design of action and listener classes instead of anonymous subclasses, you get more chance for re-use (you can already see that ComponentRelevantOnClickListener could be replaced with a "DisableOnClickListneer" that you can use anywhere for this purpose), and your code will be overall better designed.
vars that are referenced within anonymous classes need to be defined as final in Java. Jon Skeet has a great example of this nestled within this article.
Anonymous inner classes can only access variables from the outer scope if they are final. Assuming you only assign to the confirmButton once, I suggest simply tagging it as final.
final JButton confirmButton = new JButton();
Related
I have three buttons in a frame, two of which I want to edit a String with, which is a package public member in the main Class (Lab2TestDrive), something like
public class Lab2TestDrive{
...
String cale;
public static main void(String[] args){
JButton button1.. button2.. button3..
}
Can I implement an ActionListener on Lab2TestDrive, and override the actionPerformed(...) method in there? But if I do that, I don't know how I would be able to know which button triggered the actionPerformed method.
I know I could make a separate class,
public class ButtonListener implements ActionListener {
JButton button;
ButtonListener(JButton button){
this.button = button;
}
#Override
public void actionPerformed(ActionEvent arg0) {
if(button.getText().equals("Save")){
};
}
}
But then I don't know how I could access the "cale" variable :(
First: You should not let your ActionEvent be called arg0.
Then: You can technically do it all in one class. Your ActionEvent parameter has a method called getSource() which will get you the button that fired the event.
In theory, you could also create a third class storing your cale variable and give a reference to that class as a parameter to the constructor of your listener.
However, that seems very unintuitive.
You could also give a reference to your Lab2TestDrive object to the constructor of your Listener and then call a method from that class from within your actionPerformed.
Truth be told, none of those options really strikes me as great coding practice, but they should all work.
To explain what I mean by this question I will use code examples below. Imagine you have this function.
private void fadeButton(JButton b, int timeToFade) {
//Fade code goes here
}
How would you implement this as a function which could be run like
JButton b = new JButton("Press Me");
b.fadeButton(20000);
Where fadeButton now looks like
private void fadeButton(int timeToFade) {
//Fade code goes here
}
Because the function is declared on the button itself.
Typically you create a derived class:
public JFadableButton extends JButton
This will contain the method private void fadeButton(int timeToFade).
Short answer is: you don't.
Longer answer:
You can't do that in Java directly (adding methods to a class outside of the source code of that class). That might be different in other languages, like Kotlin offers "something" like that.
In java, you have to make detours, for example by turning to the decorator pattern.
And just for the record: I didn't mention the simple "you can extend that class" because I read your question as "how do I add methods to JButton directly". But of course, creating your own class that extends JButton allows you to add methods; but of course, they only exist on objects of your derived class.
You could extend JButton with a new class, thus inheriting JButton's methods and adding the ability to add your own code:
public class FadingButton extends JButton {
//Constructors go here
private void fadeButton(int timeToFade) {
//Fade code goes here
}
}
You could also decorate the JButton with another class:
public class JButtonDecorator {
private JButton btn;
//Constructor here
private void fadeButton(int timeToFade) {
//Fade code goes here, hiding the held button
}
//getter and setter method for button
}
Or, if you want lots of different ways to affect your UI, you can make a utility class, similar to above:
//You could use a factory pattern to make this a singleton instead of having static methods
public abstract class UIUtils {
private UIUtils{} //Don't instantiate this class
public static void fadeComponent(JComponent toFade) {
//Fade code goes here
}
//Other static utility methods
}
Edit: Making use of these patterns. The extended class is self-explanatory and an example of simple inheritance, so it's just a matter of JButton btn = new FadingButton(); for example. Here are the others:
To use the decorator, instantiate it at the same scope as the button you're using now. For example:
JButton myButton = new JButton();
//Customize button and add to UI
JButtonDecorator jbDec = new JButtonDecorator(myButton);
jbDec.fadeButton(20000);
Although the button is a field of the decorator, it will otherwise behave normally in your UI. The decorator just wraps the class with useful methods such as the fadeButton method.
To use the utility class, there are two ways. One is two make an abstract class with static methods (as above), some consider it bad form but it's good for simple programs:
UIUtils.fadeComponent(myButton); //It's just that simple!
//The UIUtils class itself is never instantiated.
//All the methods are static, so no instances are needed.
Or if you want a more advanced method, make your utility class a singleton. This changes the utility class to this:
public class UIUtils {
UIUtils singleton;
private UIUtils{} //Don't instantiate this class publicly
public static UIUtils getInstance() {
if(singleton==null) //This is the first time the method is called
singleton = new UIUtils();
return singleton; //Return the one instance of UIUtils
}
public void fadeComponent(JComponent toFade) {
//Fade code goes here
}
//Other utility methods
}
Then you would declare your UIUtils object at class level to use across your UI:
UIUtils uiUtil = UIUtils.getInstance();
And somewhere in your code:
uiUtil.fadeComponent(myButton);
This pattern is more efficient with memory and is more object-oriented, but I don't personally find it very suitable for utility classes.
You can create a new class which extends JButton, and then add any method that could help you achieve what you want. But that's an exemple, there is many ways to achieve this.
Ps, don't set this method as private if you want to use it somewhere else than inside your class.
This is the simplest way I think think of. You have already got it. But just use this method:
private void fadeButton(int timeToFade) {
//Fade code goes here
}
This is assuming you already have the code for the fade, do you? I think this one is the one you should use. You don't need the button to be a parameter. When you want to call the method to fade the button, just put it in the ActionListener. So after you have the ActionListener for the button, do the following: btnName.fadeButton(timeToFade);
Here is how to code the ActionListener:
btnHome.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
}
}
I have the following doubt related to how work this method:
protected JButton createToolbarButton(String name, final String id, final JPanel panel)
{
JButton button = new JButton(name); // Create a new JButton
// If the passed Jpanel named "panel" exist, add this to the JPanel named content (the CardLayout container)
if (panel != null)
content.add(panel, id);
else
button.setEnabled(false); // Otherwise disable this button
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
//System.out.println(panel.getClass());
if (panel instanceof SchedulingPanel)
((SchedulingPanel)panel).getTasksSettings().fireSettingsUpdated();
else if (panel instanceof EventsPanel)
((EventsPanel)panel).refreshPanel();
else if (panel instanceof ConfigurationPanel)
((ConfigurationPanel)panel).refreshPane();
showSection(id);
}
});
return button;
}
I have this method named CreateToolbarButton that have some input parameters including the String id parameter.
As you can see in this method I add an ActionListener inner class to my JButton object (that handles the click event on this button).
Inside this ActionListener inner class it is declared the actionPerformed() method that handle the click event and at the end of this method it call the showSection(id) method passing to id the id paramether that seems to be the same one of the createToolbarButton() input paramether.
So it seems to me that inside my ActionListener inner class I have visibility also of the paramether and variable of the container method (createToolbarButton())
Is it right? Why? it seems a litle strange to me
Tnx
Andrea
Yes, you do have visibility. this is guaranteed by the fact that those variables are final. In other words, as they do not change, the inner class won't try to refer to a variable that could die when the method createToolbarButton finishes.
If you think this behavior is strange and you do not want this, then do not use an inner class. Use a common first level class instead.
It seems to me that inside my ActionListener inner class I have visibility also of the parameter and variable of the container method (createToolbarButton()) Is it right?
Absolutely - you do have visibility of all local variables and parameters passed to the method, as long as one declares them final (as you did).
Why? it seems a little strange to me
This is a design consequence of anonymous classes not having constructors. This ability to capture locals and parameters implicitly makes it possible for you to write code that does not need your anonymous class to have a constructor.
In reality, though, your anonymous class does have a constructor. All final locals that need to be captured because you reference them from bodies of method implementations become parameters of this invisible constructor. The compiler passes these parameters implicitly, along with the reference to this of the enclosing class, and then inserts references to these captured attributes in the body of the methods that reference them.
I needed to change variables inside an inner class and I got the infamous "Cannot refer to a non-final variable inside an inner class defined in a different method" error.
void onStart(){
bt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int q = i;
}
});
}
I quickly made a class that held all of the things I wanted to change and made a final version of the class outside the inner class
class temp{
int q;
}
void onStart(){
final temp x = new temp();
bt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
x.q = i;
}
});
}
This seems to be what I need and it works but I am wondering if this is how to correctly work around the problem. Also, I really hate using the word temp to name my class. Is there an actual programming term for what I did so that I make a more descriptive name for my class?
You can simply create an inner class instead of an anonymous one (like you are currently doing). Then you have a constructor and any other methods you want to set your members. No hackiness required (like the array of 1 case).
I find this cleaner if the class requires any exchange of data with its outer class, but admit it is a personal preference. The array of 1 idiom will work as well and is more terse, but frankly, it just looks fugly. I typically limit anonymous inner classes to those that just perform actions without trying to update data in the outer class.
For example:
private MyListener listener = new MyListener();
void onStart(){
bt.setOnClickListener(listener);
}
class MyListener implements OnClickListener
{
String name;
int value;
void setName(String newName)
{
name = newName;
}
void setValue(int newValue)
{
value = newValue;
}
public void onClick(View v)
{
// Use the data for some unknown purpose
}
}
If there are multiple threads involved, then appropriate synchronization will have to be used as well.
I posted a similar answer in my other thread here. Basically the idea is to create a "wrapper" which wraps over pretty much any Object type. Since final in Java stands for "no reassignment" and not "constant", this trick pretty much works out fine. But as mentioned in the original post, make sure you tread with caution when using it in a multi-threaded environment.
I would keep a reference to your on click listener in the outer class, and make the int a member variable in your listener. Just store the variable in the listener on click, then grab the variable in the outer class when you need it, rather than setting it at the point of the click.
To put it simply, if the inner class needs to change it, make it a variable in the inner class.
Since you appear to be setting multiple things (from the comments), make a method in the main class, button1WasClicked(), (a better name might be doUpdate, doSave etc. - something relevant to what the button does), put the proper code there, and call it from the inner class / listener. (If you are using Swing I'd make it an Action, YMMV)
That way if later on there is a menu or an intent or a gesture that needs to execute the same stuff, the call is there.
I want to add event handling to buttons- and I noticed that there are two ways to do this.
Implement the ActionListener interface and then append event listeners to the buttons.
Example:
countButton.addActionListener(this);
And the in the ActionPerformed method will run to display the result.
Do not implement the ActionListener interface and instead do this:
countButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
System.out.println("You clicked the button");
}
});
How does the second method work exactly?????!!!
Thanks!
It is not necessary to define a second class for the first approach. You just need to add the
public void actionPerformed(ActionEvent e) method inside your class and do whatever you want there, after you make your class implement ActionListener. You could use a second class if you wanted to, but it is not necessary. The disadvantage is that you need to specify the source of the event with long if statements in order to take the appropriate action if you have more than one JButtons i.e.
The second approach is adding an anonymous inner ActionListener to every component. It is a more Object Oriented approach, since you have a clearer separation of the functionality of your widgets. It is of advantage to define an additional method called inside each actionPerformed, in order to be able to use this freely and refer to the containing class:
countButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
doCountButtonAction(e);
// "this" here would refer to the ActionListener (not that useful)
}
});
and implement the method:
private void doCountButtonAction(ActionEvent e) {
// do whatever you need to here
// using "this" here refers to the containing class (i.e. JFrame, JPanel or whatever)
}
Your question is not exactly on listeners as it is on how interfaces work and how you can instantiate a class in Java. Here's some finer points:
Basicaly, what the JButton class offers you is a way to declare a class who's one particular method will be called when an event is triggered on the button, like when it is clicked. If you look at the Javadocs for JButton and ActionListener, you will now how they work:
http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/AbstractButton.html#addActionListener(java.awt.event.ActionListener)
http://download.oracle.com/javase/1.4.2/docs/api/java/awt/event/ActionListener.html
What you can do here in the most old fashioned way possible is to create a class that will be triggered when someone clicks your button:
public class MyButtonActionListener implements ActionListener {
actionPerformed(ActionEvent e) {
System.out.println("Aww, you clicked!");
}
}
Now, once that's done, you can make an insance of it and add it as a listener to your button:
JButton button = new JButton("My button");
MyButtonActionListener myActionListener = new MyButtonActionListener ();
button.addActionListener(myActionListener);
On the other hand, in Java you can instantiate a class anonimousy, which means that instead of having a handler to it's instance (like myActionListener in the above code), you just instantiate it on the fly in the place you need it, and you'll have no handler to use it later. That's what's happening in your code: an ActionListener implementation is delcared on the fly as the parameter for the addActionListener method, that on the fly declaration also includes the statement that your anonymous instance is not just any class, but one that implements ActionListener, and such your anonymous declaration needs to give an implementation of the actionPerformed method.
A third option is to have a class that implements ActionListener (and the actionPerformed method), and inside that class, if you create a JButton and you want to pass it as listener an instance of the ecompasing class, you'll use "this" to reffer to that, as in :
public class MyButtonActionListener implements ActionListener {
private JButton button = new JButton();
public void init() {
button.addActionListener(this);
}
public actionPerformed(ActionEvent e) {
System.out.println("u clicked!");
}
}
There's alot of finer points to this discussion (as in how do you reffer to the "this" on a n anonymous class delcared inside an other class, and how do you reffer to the "this" of the encompassing class instance). I recommend that you read a book on the Sun Certified Java Programmer certification, it has a chapter dedicated to this
That just creates an anonymous class that implements the actionPerformed method. This way you don't have to define a new class for every place that you want to handle an event.
It works the same way as the first technique i.e. The method expects an object which implement ActionListener interface. However, both techniques has its own merits and demerits. The first technique allows utilizing single object on the expense of cluttered if/else code because same method will be handling events for multiple buttons. The second technique allows cleaner separation of logic for each button on the expense of creating multiple anonymous objects (for each button).
Either way, to add an ActionListener you need a class that implements the ActionListener interface (which defines a single method, actionPerformed). addActionListener(this) implies that this implements that interface, and any action performed will call this.actionPerformed.
In the second case, what you're doing is creating a new, anonymous, class that implements the ActionListener interface. When countButton is clicked, the actionPerformed method called is the one in the anonymous class.