How does the ActionListener work? - java

I found similar code to the following on Oracle's website. I stripped some irrelevant stuff regarding layout for space reasons.
private JTextField textField;
public class TextDemo extends JPanel implements ActionListener
{
public TextDemo()
{
textField = new JTextField(5);
//This causes a leaking this in constructor warning...
textField.addActionListener(this);
//code here for layout and to add the textfield to the panel
}
private static int ctr = 0;
#Override
public void actionPerformed(ActionEvent evt)
{
System.out.println(ctr++);
}
}
So I made a print statement to print and increment a counter to check when this actionListener is detecting an action.
I was wondering:
Is the only action that triggers this method the enter button?
In my constructor where I attached this to the action listener of the textField object, what exactly happens?

An action event occurs, whenever an action is performed by the user.
Examples: When the user clicks a button, chooses a menu item, presses Enter in a text field. The result is that an actionPerformed message is sent to all action listeners that are registered on the relevant component.
this is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this
textField.addActionListener(this); // registering actionlistener
Capturing the action event
#Override
public void actionPerformed(ActionEvent evt)
{
System.out.println(ctr++); //perform some action on click
}
http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
As suggested by Hovercraft Full Of Eels you can also use annonymous inner class as below
textField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
//do something
}
});

AFAIK, yes. The listener is called when enter is pressed while the textfield has the focus.
The current object, being constructed, is added to the list of ActionListeners of the text field, so that when enter is pressed in the text field, the current object's actionPerformed() method is called. It's generally a bad practice to pass an object not being fully constructed to another object, because the other object could call it back directly and the object wouldn't be functional, since not fully constructed yet.

First: Typically one uses a DocumentListener for a JTextField. It tells lots more interesting information about what is going on. It actually ties to the document (a sort of Model) that lies behind the GUI field.
Now:
Q1 - It's hard to figure out which mouse and keyboard actions cause the various events on the Swing screen components. As I remember, may be it for ActionPerformed. But there can be custom code added to a sub-class of a JTextField that causes an action event to fire for anything you want. You DO have to be careful if you do this.
Q2: The Listener object is stored in a list of all the objects that want to know when the text field has that action event occur. When it occurs, the text field calls the actionPerformed() method on all the objects in its listener list.
You might want to do some research on the Observer Pattern. That is the a name often used for bits of code that do this sort of thing. It can be used in many situations. The key is that it loosely couples the listener and the listenee (observer and observed). The object listening only has to tell the object to which it is listening that it want to be notified in certain cases. The object that is being listened-to keeps a list of all the various objects listening and what events they want to be informed of. That's all the connection and with the use of simple interfaces, it can be programmed simply.

Is the only action that triggers this method the enter button?
Yes. For JTextFields an ActionEvent is dispatched by pressing ENTER.
In my constructor where I attached this to the action listener of the textField object, what exactly happens?
You register the ActionListener with the component. When an ActionEvent is triggered it dispatches an ActionEvent where actionPerformed is invoked, passing it the details of the source object in the ActionEvent.
The preferred approach for the implementation of ActionListeners is to use a separate anonymous listener per component or a single concrete Action for shared functionality.

Related

JButton: actionPerformed while invisible/disabled?

Is it possible that, for example, a JButton calls actionPerformed() of an ActionListener attached to it while the button itself is hidden or has been disabled?
Like, the user clicks the button, an event gets added to the Event Queue, and in a previous event the button gets disabled.
I'd think that this would not lead to an actionPerformed(), because the user merely submitted a click- or press-event that checks all this stuff in the current JFrame.
But does anybody know if there is any case where such an unwanted situation happens? Of course, always provided that you don't do anything with Swing objects outside of the EDT.
Edit for anyone looking at this: Yes, it can indeed happen under certain circumstances. I should post an example as an answer at some point.
From the JavaDocs
public void setEnabled(boolean enabled)
Sets whether or not this component is enabled. A component that is
enabled may respond to user input, while a component that is not
enabled cannot respond to user input. Some components may alter their
visual representation when they are disabled in order to provide
feedback to the user that they cannot take input.
For more info
http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#setEnabled%28boolean%29
INFO : JButton extends AbstractButton which extends JComponent
Just use a conditional statement and tell the app to enable jButton when the conditions are met.
For example:
private buttonNameActionPerformed(java.awt.evt evt){
if(condition.equals(something)){
jButton.setEnabled(true);
}
else{
jButton.setEnabled(false);
}
}

How to add Listeners to multiple JButtons, from a view and controller, using reflection?

I'm having some difficulty implementing the MVC pattern using reflection.
I have the Model. I know nothing about this model. And I'm inferring with reflection;
I also have a View; This view will instantiate a list of objects ( JButton , JTextField , and others, ...) and also has a method to add listeners to buttons, that in turn, will invoke methods on my controller.
Then I have the controller that implements an ActionListener.
All good so far, it works... I can set the buttons programmatically , add listeners, execute methods on the controller and ask the controller to update the model, I believe I'm on the right track with MVC. However, I'm trying to do this with reflection.
E.g: I go to the Model from the Controller. I pick a given class (again, I know nothing about this class) and get ALL the setters of the class.
Then I ask the View to create a series of JTextFields (if that's the case), for all my setters, with a Save button to later on perform the setter from an action listener.
If I get 2 setters, the view creates 2 JTextFields and 2 save buttons. But how do I know, what button was pressed? In other words, how would I know what setter that button belongs to? Normally, this wouldn't be a problem, but like I said, I know nothing about the Model, so the View will might create 1, 2, 3, or a gazillion of buttons with JTextFields, JButtons and so on, ...
I'm developing a Naked Objects framework for a class project. So when I say I know nothing about the Model, is because I'm creating a GUI, based on any Model that might exist. Thus creating me some problems relating my buttons, text fields, lists in my View, to my the methods that need to be executed.
All help is greatly appreciated. Thank you!
The only way you have to distinguish two buttons is by adding state that you know that will be there when the listener you registred fires. Earlier, in my comment, I suggested that you could inherit from the control and add your state there.
public class MyButton extends JButton{
private Runnable onActionPerfomed;
public Runnable getMyAction(){
return onActionPerfomed;
}
public void setMyAction(Runnable r){
onActionPerfomed = r;
}
}
final MyButton button = new MyButton();
button.setName("button xpto");
button.setMyAction(new Runnable() {
#Override
public void run() {
System.out.println("Hey I'm running this on button " + button.getName());
}
});
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() instanceof MyButton){
MyButton btn = (MyButton)e.getSource();
btn.getMyAction().run();
}
}
});
You can achieve a effect like this one by making the Controls implement a interface instead which is more advisable.
Finally Why register the same listener for all Controls? I think that you really should register different listeners because that is what the code I provided does in a different way.

Listening and responding to events inside a Panel?

I have a MainPanel. It contains SubPanel1, SubPanel2 etc. SubPanel can have any combination of JComponents like buttons, radio buttons, text boxes etc. One SubPanel may or may not be the same as another.
How do I make the MainPanel listen and respond to the actions taking place inside the SubPanels ? Please show me some good example with commented code which teaches me how to do this.
Photo -
You may be looking for the observer pattern. Java Swing programs may use any of several ways to implement the pattern; some examples are cited here. Because a component may accept an arbitrary number of listeners, one important heuristic is to look for an existing sub-panel component to which the parent can listen. In the particular case of sub-panels, it may also be possible to forward events to a parent's listener, as suggested here.
Code is collected from Oracle JavaSE Tutorial:
public class Beeper ... implements ActionListener {
...
//where initialization occurs:
//notice this line
button.addActionListener(this);
...
public void actionPerformed(ActionEvent e) {
...//Make a beep sound...
}
}
This is how you typically register a handler right ???
What this means here ???
button.addActionListener(this);
It means ,call current object's (which this refers) *actionPerformed method whenever a action happens with that button object.
So if you pass your MainPanel's reference instead of this and your main panel has a method actionPerformed and it implements ActionListener* , whenever button fires an event , your Mainwindows's *actionPerformed** will be called.
So changed this line like this :
button.addActionListener(RefOfMainPanel);
That's all. Yup , it's that easy :)
Create your action listeners in the MainPanel and use the same in sub panels. The source of events should let you identify the sub panel from where the event is generated.

Java - Why do component functions call actionPerformed?

In my code, two comboboxes are added to actionListener( this );
In another part of my code, I call a combobox function that sets an index to a certain value. This in turn calls actionPerfoemed again and so getSource == comboBox is true. Every time I call a set function it calls actionPerformed again, creating a stack of function calls that then unwinds down to the first.
Is there a way to prevent this?
If the problem is just the initial setting, you can defer adding the listener until after both have been initialized. There's more discussion here.
From the Swing tutorial,
Combo boxes also generate item events, which are fired when any of the items' selection state changes.
These events will be generated either when a user clicks on the items with the mouse, or when your software calls setSelectedIndex().
Perhaps you don't want your actionPerformed() method in this to be called when your software calls setSelectedIndex(). You may need a Boolean eventInitiatedBySoftware. In your main (this) class, you could say
synchronized(eventInitiatedBySoftware) {
eventInitiatedBySoftware=true;
comboboxeditor.setSelectedIndex(n);
}
and then in your listener:
public void actionPerformed(ActionEvent ae) {
synchronized(eventInitiatedBySoftware) {
if (eventInitiatedBySoftware) {
eventInitiatedBySoftware=false; // clear your flag.
return; // don't want to process this event.
}
// the rest of your method goes here
}
When your software wants to adjust the value, it will set the Boolean to true. The actionPerformed method will be called, but your test will realise that this event was initiated by the software, and return before doing any of your existing code. It will clear the Boolean, so that if a user now uses the mouse to perform a selection action, your code will realise that it wasn't softwareInitiated.
BTW, It's possible that you misunderstand the event concept. For example, I suspect you are actually adding "this" as an event listener for each combobox, rather than adding comboboxes as listeners to "this". You might like to look at the Writing Event Listeners trail.

How to update JTextArea in Java Swing?

I have a JComboBox named "jComboBox18" and a JTextArea "jTextArea11". Now I want that whenever a item is selected from the "jComboBox18" combo box its corresponding description is shown in the "jTextArea11" textarea.
I have added the appropriate listener to the JComboBox But the JTextArea is not showing any text. The code that I have written is as follows:
private void jComboBox18ItemStateChanged(java.awt.event.ItemEvent evt) {
Object item = jComboBox18.getSelectedItem();
if(item != null) {
ems.logic.Process selectedProcess = (ems.logic.Process)item;
jTextArea11.setText(selectedProcess.getProcessDescription());
jTextArea11.updateUI();
jTextArea11.revalidate();
jTextArea11.validate();
}
}
=====================EDITED===========================================
The method is being called for sure. I am changing the state of one more combobox
which is also being written in this method and its state changes successfully whenever item is selected from the "jComboBox18"
I think That should work. In fact, you should only need the setText() call. My guess is that you're function isn't getting called for some reason. Put a break point in your code and make sure it's getting called.
In the code shown your method is named as jComboBox18ItemStateChanged. Are you sure this method is being called. The ItemListener for a JComboBox should implement the interface ItemListener which declares that the subclasses should implement the below method.
void itemStateChanged(ItemEvent e);
How are you adding an instance of ItemListener to your JComboBox ?
EDIT:
After reading your edit and comments one another possiblity that I can think of is that:
you have a listener that is triggered when the textarea is updated and probably itis undoing the changes done in JComboBox listener.

Categories

Resources