Swing:the role of action listener - java

# In the problem which i am posting,in this i am unable to understand the code mentioned on line 1 and line 2, As what i know about them is that they are used to set the action listener for the button but the thing which is most confusing for me is that in the syntax sated on the line 1 and line 2 as, {JB1.addActionListener(this)} in this what is the role of "this".....so please tell the basic behind this.And how this whole syntax works...in detail. #
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class frametest1_1 implements ActionListener
{
JLabel JL;
public frametest1_1()
{
//create a JFrame container
JFrame JF=new JFrame("A BUTTON");
//Frame Layout This is contained in Java .awt.*; "ON USING THIS OPTION OUR BUTTON AND OTHER COMPONENT ARE ADJUSTED IN THE FRAME AUTOMATICALLY"
JF.setLayout(new FlowLayout());
//set the size of the container
JF.setSize(200, 200);
//set visible
JF.setVisible(true);
//make button
JButton JB1=new JButton("FIRST");
JButton JB2=new JButton("SECOND");
//add button to the JFrame container
JF.add(JB1);
JF.add(JB2);
//Create and add Label to the JFrame container
JL=new JLabel("PRESS A BUTTON");
JF.add(JL);
//set action command :now this will help in determining that which button is presses, is it FIRST or SECOND
JB1.setActionCommand("one");
JB2.setActionCommand("two");
//The action responded is added to the actionlistener
JB1.addActionListener((ActionListener) this); // line 1
JB2.addActionListener((ActionListener) this); // line 2
}
public void actionPerformed(ActionEvent ae)
{
if(ae.getActionCommand().equals("one"))
JL.setText("First Button Pressed"); // to set text on the label
else
JL.setText("Second button Pressed"); // to set the text on the label
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new frametest1_1();
}
});
}
}

1. Consider that Listener is someone who reacts to some action.
2.ActionListener is an Interface which has a call back method, named actionPerformed, inside which is the code that will run when certain action is done on the controller.
3. This line JB1.addActionListener((ActionListener) this); means as follows
JB1 - Button
ActionListener - Interface
addActionListener - Registering the Button with the Listener.
4. addActionListener will bind/register the Button with the Listener (here its ActionListener).
5. In an MVC architecture Button is the controller, when certain action is done on it, then who all are to be informed to do certain things is done by registering it to the listener.
6. And this Listener will have the callback method which will be overridden by the class that implements the listener.
7. Moreover in your example, you can also do it like this...
JB1.addActionListener(this);

'this' refers to the current instance of the enclosing class (frametest1_1), the instance constructed in the run() method.
Some further reading on 'this':
Using the this Keyword

Provided code is terribly ugly from object-oriented design point of view - hence your confusion. It's definitely bad idea to have one class responsible for:
containing main method
holding references to JFrame components (i.e. JLabel)
implementation of ActionListener concept
thus violating Single responsibility principle, though such ugly examples can be found even in official tutorials on Swing.
Slightly better implementation of exactly the same functionality would be the following:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class frametest1_1 {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
runFrameTest();
}
});
}
public static void runFrameTest() {
//create a JFrame container
JFrame JF=new JFrame("A BUTTON");
//Frame Layout This is contained in Java .awt.*; "ON USING THIS OPTION OUR BUTTON AND OTHER COMPONENT ARE ADJUSTED IN THE FRAME AUTOMATICALLY"
JF.setLayout(new FlowLayout());
//set the size of the container
JF.setSize(200, 200);
//set visible
JF.setVisible(true);
//make button
JButton JB1=new JButton("FIRST");
JButton JB2=new JButton("SECOND");
//add button to the JFrame container
JF.add(JB1);
JF.add(JB2);
//Create and add Label to the JFrame container
final JLabel JL=new JLabel("PRESS A BUTTON");
JF.add(JL);
//set action command :now this will help in determining that which button is presses, is it FIRST or SECOND
JB1.setActionCommand("one");
JB2.setActionCommand("two");
ActionListener labelUpdater = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
if(ae.getActionCommand().equals("one"))
JL.setText("First Button Pressed"); // to set text on the label
else
JL.setText("Second button Pressed"); // to set the text on the label
}
};
//The action responded is added to the actionlistener
JB1.addActionListener(labelUpdater); // line 1
JB2.addActionListener(labelUpdater); // line 2
}
}
Hope this helps in understanding ...

Related

ActionListener never fires if previous listener calls revalidate()

So I've made a basic UI that shows text fields that will alert the user if the contents is outside of given bounds by causing a label to appear below it. This occurs if the user hits return or Tabs away from the field.
There is also a button that will advance the program so long as all of the fields are correct. If they are not it will cause the text fields to 'flash' (animation background red back to white).
This works fine except for one little bug - if the user enters incorrect data and immediately clicks the button, without first leaving the field, the focus listener fires and adds the label below the text field, but the click button action never fires (no flash). This appears to be due to the FocusListener event calling revalidate() on the panel and or pack() on the enclosing jframe.
Here's a working minimal example to demonstrate the problem
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
/**
* Demonstration that running revalidate() or JFrame.pack() in FocusListener
* (on a textField) causes the action listener for a JButton to never fire.
*
* This is only a problem if the contents of the frame are changed (ie. only
* happens once when label is added).
*/
public final class App {
private App() {
JFrame jFrame = new JFrame();
JPanel main = new JPanel();
MyPanel myPanel = new MyPanel();
JButton button = new JButton("Click");
button.addActionListener(event->{
/* with the code as it is this will never print if
the button is pressed while textField has focus
and label is not yet displayed */
System.out.print("\n Click has been pressed");
});
main.add(myPanel);
main.add(button);
jFrame.add(main);
jFrame.pack();
jFrame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new App());
}
/**
* Creates a panel containing a text box.
* when this text box looses focus it causes
* a label to be added and the frame to resize (.pack())
*/
class MyPanel extends JPanel{
JLabel label;
JTextField textField;
public MyPanel(){
label = new JLabel("hello");
textField = new JTextField(10);
this.add(textField);
textField.addFocusListener(new Handler(this));
}
private class Handler implements FocusListener{
JPanel panel;
public Handler(JPanel panel){
this.panel = panel;
}
#Override
public void focusGained(FocusEvent e) {
}
#Override
public void focusLost(FocusEvent e) {
// if this line is removed
panel.add(label);
// if the below two lines are removed
// button ActionListener will fire
panel.revalidate();
SwingUtilities.getWindowAncestor(panel).pack(); // side note if this is called is revalidate() needed?
}
}
}
}
Any advice on how to fix this would be great. One thing to note is that in the real program the MyPanel class would be in its own file, if that affects any solutions.
Cheers.
=============== EDIT ======================
I was running it in my IDE (vscode) where the problem occured. When I compiled it and ran it from the terminal it worked fine. I presume its some quirk of the IDE.

ActionListener interface

public class myWindow extends JFrame implements ActionListener{
if i have this code my class will be a JFrame where where I can add component and add actionlistener to them in my constructor as followed
public MyWindow()
{
JButton b = new Jbutton("button");
b.addActionListener(this);
}
this keyword will work as an anonymous actionlistener object(which is my class) right ?
later on i will override the actionPerformed method with the heading:-
public void ActionPerformed(ActionEvent ae)
{ :
:
}
I really have a big confusion here .. my book says "the listener object invokes an event handler method with the event as an argument "
listener object : this
event handler method :ActionPerformed(ActionEvent ae)
argument: my event is the JButton b .. how come when it's not of EventAction type ? And if so why we are using :
ae.getActionCommand();
I thought it's a method to tell which component fired the event,why we need it when the component is passed as an argument then?
this keyword will work as an anonymous actionlistener object(which is my class) right ?
No this will be an Object of class which is implementing the actionsListener. In your case it is "MyWindow".
my event is the JButton b .. how come when it's not of EventAction type ? And if so why we are using :
JButton b is a component not an event. Events describes the changed in state of the source. When users interact with GUI, events are generated like clicking of button, moving the mouse.
Reference from Click here
Event Handling is a mechanism that controls the event and decides what should happen if an event occurs.
Steps involved in event handling:-
The User clicks the button and the event is generated.
Now the object of concerned event class is created automatically and information about the source and the event get populated with in same object.
Event object is forwarded to the method of registered listener class.
the method is now get executed and returns.
I thought it's a method to tell which component fired the event,why we need it when the component is passed as an argument then?
Now you would have understood that there could be many buttons registered to the same ActionsListerner. Now to perform different actions for different events,
e.getActionCommand() comes handy, it will tell you which button is the source of firing the event.
Hope this helps.
I have tried to give you can example of a simple JButton program.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonSwing {
private int numClicks = 0;
public Component createComponents() {
//Method for creating the GUI componenets
final JLabel label = new JLabel("Clicks: " + "0"); //final so that i can access inside inner class
JButton button = new JButton("Simple Button");
button.addActionListener(
//inner class for ActionListener. This is how generally it is done.
new ActionListener() {
public void actionPerformed(ActionEvent e) {
numClicks++;
label.setText("Clicks: " + numClicks);
System.out.println(e.getActionCommand());
System.out.println(e.getModifiers());
System.out.println(e.paramString());
}
}
);
JPanel pane = new JPanel(); //using JPanel as conatiner first.
pane.setLayout(new FlowLayout());
pane.add(button); // adding button to the JPanel.
pane.add(label); // adding label to the JPanel.
return pane;
}
public static void main(String[] args) {
JFrame frame = new JFrame("SwingApplication");
ButtonSwing obj = new ButtonSwing();
Component contents = obj.createComponents();
frame.getContentPane().add(contents);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack(); //It will cause the window to be sized to fit the preferred size
//and layouts of its subcomponents.
frame.setVisible(true);
}
}
Your JButton is a component not an event. Events are generated by some action on components in this case When you click your button, an ActionEvent will be fired and it will be passed to all the listeners who subscribed to that event in this case it is your MyWindow object which is serving as an ActionListener

Where have I messed up regarding creating a Game Menu?

I am trying to create a basic game menu for a game right now. I am just testing out the menu for now, and most of the options I wrote are just to test out whether the menu actually works or not. So I have a Menu class and a OptionPanel class as well.
Here is the Menu Class:
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class Main extends JFrame {
JPanel cardPanel;
public Main(String title) {
super(title);
setBounds(100, 100, 800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cardPanel = new JPanel();
CardLayout cl = new CardLayout();
cardPanel.setLayout(cl);
OptionPanel panel1 = new OptionPanel(this);
Board panel2 = new Board();
Rules panel3 = new Rules();
cardPanel.add(panel1,"1");
cardPanel.add(panel2,"2");
cardPanel.add(panel3,"3");
add(cardPanel);
setVisible(true);
}
public static void main(String[] args)
{
Main w = new Main("AP Animation Demo");
}
public void changePanel() {
((CardLayout)cardPanel.getLayout()).next(cardPanel);
requestFocus();
}
}
And here is my Option Panel class:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class OptionPanel extends JPanel implements ActionListener {
Main w;
public OptionPanel(Main w) {
this.w = w;
JButton button = new JButton("Press me!");
button.addActionListener(this);
add(button);
JButton button2 = new JButton("Game rules");
button2.addActionListener(this);
add(button2);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
setBackground(Color.BLACK);
}// Call JPanel's paintComponent method to paint the background
public void actionPerformed(ActionEvent e) {
w.changePanel();
}
}
How do I make it so when the menu pops up, I can click on one button that leads to the game, and when clicking on another button, get linked to another screen. I think it has something to do with the actionPerformed thing, so I tried adding if (e.getSource == button) and stuff like that, but it could not find any button variable. Any advice/feedback?
If you want the actionPerformed() method to be able to access a button variable, then the variable has to have an instance scope (or static, less preferable almost always). Referring to it in the method as you have it written won't work because the button variable is local to the constructor.
The suggestion in the comments is to make a separate ActionListener for each button; you only need to use the if (e.getSource() == button) if the one actionPerformed() method is getting called for multiple buttons. The difference between these is a little much for a SO answer; you can get a tutorial on action listeners in the Java tutorials at Oracle.
The way you have started above suggests you are going to use the OptionPanel as a single action listener for all buttons, and therefore it needs to test which button invoked it. If instead you have a separate action listener for each button, then it knows which button invoked it and doesn't need to test.
Try looking up "anonymous inner classes" as they relate to action listeners in Java.

Java GUI: How to Set Focus on JButton in JPanel on JFrame?

I've experimented and searched and I can't seem to figure out what I thought would be something simple, which is having my START button have focus when my little GUI app launches I.e., so all the user has to do is press their Enter/Return key, which will have the same effect as if they had clicked the START button with their mouse. Here is my code. Thanks for your help :)
private void initialize() {
// Launch the frame:
frame = new JFrame();
frame.setTitle("Welcome!");
frame.setSize(520, 480);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add the image:
ImageIcon heroShotImage = new ImageIcon("heroShot.jpg");
JPanel heroShotPanel = new JPanel();
JLabel heroShot = new JLabel(heroShotImage);
heroShotPanel.add(heroShot);
// Create a panel to hold the "Start" button:
JPanel submitPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
// Create the "Start" button, which launches business logic and dialogs:
JButton start = new JButton("Start");
start.setToolTipText("Click to use library");
start.setFocusable(true); // How do I get focus on button on App launch?
start.requestFocus(true); // Tried a few things and can't get it to work.
// Listen for user actions and do some basic validation:
start.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// THE APP's LOGIC GOES HERE...
}
// Finish setting up the GUI and its components, listeners, and actions:
submitPanel.add(start);
frame.getContentPane().add(heroShotPanel, BorderLayout.NORTH);
frame.getContentPane().add(submitPanel, BorderLayout.SOUTH);
}
Try out this code.. All I have done is moving the requestFocus() method at the end.
Basically these are the two things you have to do for it to respond while pressing enter key and for it to be focused by default.
frame.getRootPane().setDefaultButton(start);
start.requestFocus();
package sof;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestFrame {
public static void main(String[] args) {
// Launch the frame:
JFrame frame = new JFrame();
frame.setTitle("Welcome!");
frame.setSize(520, 480);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add the image:
ImageIcon heroShotImage = new ImageIcon("heroShot.jpg");
JPanel heroShotPanel = new JPanel();
JLabel heroShot = new JLabel(heroShotImage);
heroShotPanel.add(heroShot);
// Create a panel to hold the "Start" button:
JPanel submitPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
JButton start = new JButton("Start");
start.setToolTipText("Click to use library");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("I AM PRESSED");
}
});
submitPanel.add(start);
frame.getContentPane().add(heroShotPanel, BorderLayout.NORTH);
frame.getContentPane().add(submitPanel, BorderLayout.SOUTH);
frame.setVisible(true);
frame.getRootPane().setDefaultButton(start);
start.requestFocus();
}
}
If I'm understanding you then you want to do a click event of Start Button when user hits Enter key. If this is the case then you can do it as follows:
jFrame.getRootPane().setDefaultButton(start);// 'start' will be your start button
And if you just want to get focus on start Button then shift your requestFocus() method at the end (after you make your frame visible) and no need to pass true in it. Also it is better to use requestFocusInWindow() then requestFocus() as stated in java doc.
move your focus line to the end of the method
and change it to
start.requestFocus(); // without params
If you want your start button to get the focus then do this at the end
//This button will have the initial focus.
start.requestFocusInWindow();
not easy job because Focus/Focus_SubSystem came from Native OS and is pretty asynchronous,
1) inside one Containers works by wraping that into invokeLater(),
2) manage Focus betweens two or more Top-Level Containers, by #camickr

Performing action when closing JFrame

In my program I have a main JFrame that holds a button. When this button is clicked a new JFrame appears in which I can change some information. Whenever I finish editing I press a save button on the new JFrame which saves the changes and disposes the JFrame. Now when this is done, I'd like to perform an action in the main JFrame as well, but only if something changed. If I open the new JFrame and just close it again without using the save button, I don't want to do anything in the main frame.
I've tried searching the web for a solution, but just don't seem to be anything useful out there..
An example of the code I've got so far:
Main Frame...
public class MainFrame extends JFrame
{
public MainFrame()
{
super("Main Frame");
JButton details = new JButton("Add Detail");
add(details);
details.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
new DetailFrame().setVisible(true);
}
});
}
}
Detail Frame...
public class DetailFrame extends JFrame
{
public DetailFrame()
{
super("Detail Frame");
JButton save = new JButton("Save");
add(save);
save.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
// Save whatever content
dispose();
}
});
}
}
So when I click the "Save" button on the Detail Frame, I want to do something in the Main Frame, whereas when the "x" is clicked on the Detail Frame, I don't want to do anything..
Hope someone is able to help me, and sorry for my english..
You can pass a MainFrame handle to the DetailFrame constructor. Then, on clicking the Save button, the DetailFrame would call a function in MainFrame and pass the changes to it.
Another way is to create a public boolean variable in DetailFrame and set it to true when the Save button is clicked. This way MainFrame will know whether the DetailFrame was closed or Save'd.
EDIT: Some more ideas:
Use JDialog instead of JFrame. JDialog.setVisible is modal, i.e. it will block the calling function until the dialog is closed; this way you can process the results of the dialog in the same "Details" button listener.
To access the dialog after it is called, store the dialog in a separate variable. First construct the dialog, then show it, and then process the result by analyzing its variables.
Store the results of editing in other public variables of DetailFrame (or let's call it DetailDialog). This should happen only when the "Save" button is clicked. This may even allow to go without the boolean variable (depends on the types of values you are editing).
DetailDialog dlg = new DetailDialog();
dlg.setVisible(true);
if(dlg.approvedResult != null) {
// process the result...
}
EDIT: Sorry, JDialog is not modal by default. Need to call a special super constructor to make it modal.
Also, here you will have to pass the reference to MainFrame to the dialog constructor, but you still can declare it as a simple JFrame and avoid unnecessary dependencies.
To get the reference to the enclosing MainFrame from within the anonymous ActionListener, use MainFrame.this.
To be able to change the button text after it was created, you will have to store the button in a member variable.
Main Frame...
public class MainFrame extends JFrame
{
private JButton details = new JButton("Add Detail");
public MainFrame()
{
super("Main Frame");
getContentPane().add(details);
details.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
DetailDialog dlg = new DetailDialog(MainFrame.this);
dlg.setVisible(true);
if(dlg.approved){
details.setText("Edit Detail");
}
}
});
}
}
Detail Dialog... (not Frame)
public class DetailDialog extends JDialog
{
public boolean approved = false;
public DetailDialog(JFrame parent)
{
super(parent,"Detail Dialog",true); // modal dialog parented to the calling frame
JButton save = new JButton("Save");
getContentPane().add(save);
save.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
// Save whatever content
approved = true;
dispose();
}
});
}
}
Create the detail frame in the main frame, and add a windowlistener to it, using the windowadapter class. Implement the windowclosing event by checking for changes, handle those, and then dispose the detail frame. This is all done in the mainframe.
The detail frame should have do nothing on close set to prevent the detail frame being disposed before you recorded the changes.
You may wish to implement checking for changes in the detailframe as a method returning a class holding the interesting data. That way your windowlistener can be small an to the point.
Forget the 2nd JFrame. use a modal dialog instead. It will block input until dismissed. Once dismissed, the only thing to do is decide whether to update the original data. JOptionPane has some inbuilt functionality that makes that easy. If the user presses Cancel or the esc key, the showInputDialog() method will return null as the result.
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
class EditInfo {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
final JFrame f = new JFrame("Uneditable");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel p = new JPanel(new BorderLayout(10,10));
final JTextField tf = new JTextField("Hello World!", 20);
tf.setEnabled(false);
p.add(tf, BorderLayout.CENTER);
JButton edit = new JButton("Edit");
edit.addActionListener( new ActionListener(){
public void actionPerformed(ActionEvent ae) {
String result = JOptionPane.showInputDialog(
f,
"Edit text",
tf.getText());
if (result!=null) {
tf.setText(result);
}
}
} );
p.add(edit, BorderLayout.EAST);
p.setBorder(new EmptyBorder(10,10,10,10));
f.setContentPane(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
If it is necessary to edit a number of fields all at once in the JOptionPane, use a JPanel to contain them all, and put them in a showMessageDialog() call. Check the integer based return result to determine if the user OK'd the changes.

Categories

Resources