Using actionListener with Button - java

I am programming a menu to be used with a formerly all text based game. I am trying to use addActionListener to print a line of text when a button is clicked, so I can figure out how to implement my main code in the future. The issue I am having is with the addActionListener method on my JButton. I am performing all of this with a JFrame. From what others say, I have used this as the argument but am getting a "non-static variable this cannot be referenced from a static context" error. Here is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Menu
{
public static void Menu()
{
JButton button = new JButton("Click to enter");
button.setBounds(125, 140, 150, 20);
JFrame frame = new JFrame("Casino");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(button);
button.addActionListener(this);
JLabel emptyLabel = new JLabel("");
emptyLabel.setPreferredSize(new Dimension(400, 300));
frame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
#Override
String s = "Welcome!";
System.out.println(s);
}
}

Currently your program is nothing more than a single static method with everything trying to be shoehorned into this method. This would be fine if you were creating the most basic console program, such as one that asks the user for 2 numbers, then adds the numbers and returns the answer, but you are no longer trying to do this. Instead you're trying to create a Swing GUI program, one whose state you wish to change if the user interacts with it in an event-driven way, in other words you want it to change state if the user presses a button or selects a menu item.
Your problem is that you're trying to mesh this simple static world with the "instance" world, but in static land, there is no this.
Since your needs and requirements are becoming more complex, your program structure will need to change to reflect this. Is this an absolute requirement that you do this? No -- something called Turing Equivalence tells that it is possible to write most complex program imaginable inside of a single static main method, but due to the increased complexity, the program would become very difficult to understand and almost impossible to debug.
What I recommend specifically is that you create one or more well behaved object-orient classes, classes with non-static variables and non-static methods, and use these to build your GUI and its model (the non-GUI nucleus that GUI programs should have). Again the main method should be short, very short, and should only involve itself in creating the above classes, and setting the GUI visible, and that's about it.
What you want to do is to study the basic concepts of Java, and in particular that on how to create Java classes. The Java tutorials can help you with this.

There are a number of issues with this class.
It does not implement ActionListener so it can't be used as parameter to JButton
The static modifier on the Menu method means you can even use this anyway
public static void Main is not a constructor, so beware
#Override should appear before the method declaration, not in it.
Something like...
public class Menu implements ActionListener
{
public Menu()
{
JButton button = new JButton("Click to enter");
button.setBounds(125, 140, 150, 20);
JFrame frame = new JFrame("Casino");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(button);
button.addActionListener(this);
JLabel emptyLabel = new JLabel("");
emptyLabel.setPreferredSize(new Dimension(400, 300));
frame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e)
{
String s = "Welcome!";
System.out.println(s);
}
}
Might be a better approach, but I'd be worried about creating a JFrame within the class, but that's just me...

Related

Style errors with swing: method creates an object but does not assign variable or field

"This method creates an object but does not assign this object to any variable or field. This implies that the class operates through side effects in the constructor, which is a bad pattern to use, as it adds unnecessary coupling. Consider pulling the side effect out of the constructor, into a separate method, or into the calling method."
This short test program runs as I expected, but I don't know how to address this checkstyle error. Most of the examples of using javax.swing seem to have this structure.
There is also a error causes by EXIT_ON_CLOSE, but without it the process lingers after I close the window and must be force quit.
public class GUI implements ActionListener {
private int clicks = 0;
private JLabel label = new JLabel("Clicks= " + clicks);
private JFrame frame = new JFrame();
public GUI() {
// make a Jbutton named button
JButton button = new JButton("Click Me");
button.addActionListener(this);
// arrange the button and label
JPanel panel = new JPanel();
panel.add(button);
panel.add(label);
// put the panel in a frame
frame.add(panel, BorderLayout.CENTER);
// EXIT_ON_CLOSE has a style error too.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Graphical User Interface");
frame.pack();
frame.setVisible(true);
}
// update label and number of clicks when button is clicked
public void actionPerformed(ActionEvent e) {
clicks++;
label.setText("Clicks= " + clicks);
}
// This is the code that InteliJ says has bad form.
public static void main(String[] args) {
new GUI();
} }
One bad pattern here is that you are not creating your GUI on Event Dispatch Thread. Not sure if this is related to your problem.. but you should always use EDT to create the GUI in Swing.
EDIT: take a look here. In the main method you just have "new GUI()" without a reference variable. Writing your app like this example you are not going to use the constructor to create all :)

Cannot refer to the non-final local variable display defined in an enclosing scope

This might be a very basic question. But I am stuck at this. The error that I get for the String variable display states:
Cannot refer to the non-final local variable display defined in an enclosing scope.
If I use a final keyword, I get the message:
The final local variable display cannot be assigned, since it is defined in an enclosing slope.*
The code is:
public class Frame {
public static void main(String[] args) {
String display=" ";
Frame ob=new Frame();
JFrame frame=new JFrame("Test");
frame.setBounds(300,100,800,500);
//Container c=frame.getContentPane();
frame.setLayout(null);
final JTextField name=new JTextField();
name.setBounds(500,212,150,20);
JLabel nameLabel=new JLabel("Name: ");
nameLabel.setForeground(Color.WHITE);
nameLabel.setBounds(450,171,100,100);
JTextField ohr=new JTextField();
ohr.setBounds(500,282,150,20);
JLabel ohrID=new JLabel("OHR ID: ");
ohrID.setForeground(Color.WHITE);
ohrID.setBounds(450,241,100,100);
final JButton button=new JButton("Submit");
button.setBounds(530,350,90,20);
frame.add(name);
frame.add(ohr);
frame.add(ohrID);
frame.add(nameLabel);
frame.add(button);
frame.getContentPane().setBackground(Color.DARK_GRAY);
frame.setVisible(true);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
if(e.getSource()==button){
display=name.getText();
JOptionPane.showMessageDialog(null, "Hi "+ display);
System.exit(0);
}
}
});
}
Thanks in advance!
There are multiple issues with your code, and we'll address them right here, right now and solve your problem at the same time.
public class Frame { this particular line has an error, Frame is the name of an AWT class, so it might confuse you or anyone who reads this code later on, give it a more meaningful name and avoid those names that could be confused with other Java packages.
Frame ob=new Frame(); you create an instance of your class and never use it again, why?
frame.setLayout(null); NEVER, please don't use null-layout, Swing has to deal with multiple PLAFs, screen sizes and resolutions, different OS, pixel perfect apps might seem like the easiest way to create complex UIs but later on you'll find that errors like this happen very often.
.setBounds(...) on every component, again, this is due to null-layout but it's better to use Layout managers
final JTextField name=new JTextField(); There's no need to declare any of your components as final, this is due to a poor design of your class, your components should be declared as class members (outside any method including main).
Speaking about main, separate your program into smaller pieces, don't throw everything at main or at the very least create a method that is not static so you can call it after creating an instance of your class (or else later on you'll end up with tons of static variables and that's a poor design of your class once again).
System.exit(0); it will stop the JVM, it's never a good idea to do that, it's better to .dispose() the JFrame and have your JFrame's defaultCloseOperation set to EXIT_ON_CLOSE which will safely dispose your app and then stop the JVM.
display=name.getText();, for this particular case, display could be an inner variable rather than a class member. This will solve your particular question
JOptionPane.showMessageDialog(null, "Hi "+ display); that null should be a reference to your JFrame, this will place your dialog in the middle of that JFrame rather than in the middle of the screen.
You never place your program inside the EDT, see point #2 in this answer.
So, having all the above points in mind, here's an improved version of your code.
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class UsingVariablesInsideActionListenerExample {
//We declare our components here
private JFrame frame;
private JButton button;
private JTextField name;
private JTextField ohr;
private JLabel nameLabel;
private JLabel ohrID;
private JPanel pane;
private JPanel namePane;
private JPanel ohrPane;
public static void main(String[] args) {
SwingUtilities.invokeLater(new UsingVariablesInsideActionListenerExample()::createAndShowGUI); //This is using Java 8 lambdas to place your program in the EDT
}
private void createAndShowGUI() {
frame = new JFrame("Test"); //Create your JFrame
pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS)); //This will make this JPanel to arrange components vertically
namePane = new JPanel(); //By default, JPanels have FlowLayout which will arrange components horizontally
ohrPane = new JPanel();
name = new JTextField(10); //We create a JTextField with 10 columns
nameLabel = new JLabel("Name: ");
nameLabel.setForeground(Color.WHITE);
ohr = new JTextField(10);
ohrID = new JLabel("OHR ID: ");
ohrID.setForeground(Color.WHITE);
button = new JButton("Submit");
//Add the action listener
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
String display = name.getText(); //The display variable is now an inner variable rather than a class member
JOptionPane.showMessageDialog(frame, "Hi " + display);
frame.dispose(); //We dispose the JFrame and it will be closed after due to EXIT_ON_CLOSE below.
}
}
});
//We add the components to the namePane (horizontally), the order matters
namePane.add(nameLabel);
namePane.add(name);
//Now we add these components to the ohrPane (horizontally again)
ohrPane.add(ohrID);
ohrPane.add(ohr);
//We then add the name and ohr panes to a bigger JPanel (pane, which if you remember will add them vertically) and we add the button at the end
pane.add(namePane);
pane.add(ohrPane);
pane.add(button);
//We make them non opaque (transparent) so that we can see the background color of the JFrame
namePane.setOpaque(false);
ohrPane.setOpaque(false);
pane.setOpaque(false);
frame.add(pane);
frame.getContentPane().setBackground(Color.DARK_GRAY);
frame.pack(); //This will get every component's preferred size and make the JFrame as small as possible where it looks good on every OS, PLAF, screen size and resolution.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true); //We make the frame visible (always at the very end, when we've added everything to it).
}
}
And this is how it looks like now.
The UI may not be perfectly equal to the one you have, but I'm sure you can play with the different layout managers, and nest various JPanels to get a much better looking UI than mine, or at least a more similar one to the one you had.
Variable used in side an inner class should be effectively final . You can use a string[] of length 1 instead of string to resolve this . Please read bellow post for more details
Difference between final and effectively final
Also check this post for more details
Variable used in lambda expression should be final or effectively final

Why does my button disappear from my borderlayout when I add an actionListener to it?

I am writing the back end code for my java project which is implementing a fitness logger. For some reason when I put the actionListener function to a button in my Border Layout then the button disappears.
I tried setting the function in different places in the constructor.
public class Buttons extends JFrame implements ActionListener {
JFrame frame = new JFrame("Menu");
JPanel MyPanel= new JPanel();
JButton b1= new JButton("Daily Logger");
JButton b2= new JButton("View Weekly Logs");
JButton b3= new JButton("Weight Calculator");
JButton b4= new JButton("BMI Calculator");
JButton b5= new JButton("Log Out");
public Buttons(){
MyPanel.setLayout(new BorderLayout());
MyPanel.add(b1, "North");
MyPanel.add(b2, "Center");
MyPanel.add(b3, "East");
MyPanel.add(b4, "West");
MyPanel.add(b5, "South");
b1.addActionListener(this);
add(b1);
frame.getContentPane().add(MyPanel, "North");
frame.setSize(500,115);
frame.setVisible(true);
}
public static void main(String[] args) {
new Buttons();
}
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if(command.equals("Daily Logger"))
myMethod();
}
public void myMethod() {
JOptionPane.showMessageDialog(this,"Onto the next step");
}
}
I expected the button to show up in the border layout when I add the actionListener function but it disappears. If the button did work as it should it should implement the myMethod() function. My main goal is to show my JTable that I created in another class to show up when the button is pressed.
add(b1);
Should be removed. The code has already added it via:
MyPanel.add(b1, "North");
A BorderLayout accommodates up to five components, each in a separate layout constraint. If a component is added twice, in different areas, it faces two problems:
A component can only appear in one place.
The component 'overwrites' the original component added to that area of the layout.
More general tips:
Please learn common Java nomenclature (naming conventions - e.g. EachWordUpperCaseClass, firstWordLowerCaseMethod(), firstWordLowerCaseAttribute unless it is an UPPER_CASE_CONSTANT) and use it consistently.
For better help sooner, add a minimal reproducible example or Short, Self Contained, Correct Example. Note: The posted code only needed appropriate import statements to be an MRE / SSCCE.
MyPanel.add(b3, "East"); re East
There are constants for this. E.G. BorderLayout.EAST. always use the constants for compile time checking.
But BorderLayout.LINE_END is sensitive to the locale. It will appear on the RHS for left to right languages, and the left for right to left languages.
All Swing & AWT GUIs should be created & updated on the EDT (Event Dispatch Thread).
The code both extends and keeps a reference to, JFrame. Keep the latter, ditch the former.
Be sure to add the java (language) and swing (GUI toolkit) tags to questions! The only reason I saw this was because I (extraordinarily) checked the question listing for the jframe tag! To the best of my recollection, it's the first time I've ever checked that tag's question listing. Even more ironic, it did not make the list of 5 tags I saw as being most relevant to this question.
frame.getContentPane().add(.. can be shortened to frame.add(.. since Java 1.5.
frame.setSize(500,115); should better be frame.pack();, since 500 x 115 is no better than a guess, and will be 'wrong' for different OS' (the size of the content pane will change due to different frame decorations per system).

Java GUI Design: Use object or use method?

I have a noobie question about GUI designing in Java: should we use more methods or more objects? Take a very simple example: dialogs.
For a dialog, I can use a method or use an object:
Using Method:
public static void MakeDialog() {
final JFrame frame = new JFrame();
String help = "";//Reader.readhelp();
JTextArea contentArea = new JTextArea(help, 60, 60);
JScrollPane scroller = new JScrollPane(contentArea);
scroller.setPreferredSize(new Dimension (650,650));
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent a) {
frame.dispose();
}
});
JPanel panel = new JPanel();
panel.add(scroller);
panel.add(close);
frame.add(panel);
frame.setTitle("Help");
frame.setSize(700, 700);
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
Then I will just need to call this method need this dialog to show up.
However, I can have another approach: make a class of dialog:
public class MyDialog {
public MyDialog() {
final JFrame frame = new JFrame();
String help = "";//Reader.readhelp();
JTextArea contentArea = new JTextArea(help, 60, 60);
JScrollPane scroller = new JScrollPane(contentArea);
scroller.setPreferredSize(new Dimension (650,650));
JButton close = new JButton("Close");
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent a) {
frame.dispose();
}
});
JPanel panel = new JPanel();
panel.add(scroller);
panel.add(close);
frame.add(panel);
frame.setTitle("Help");
frame.setSize(700, 700);
frame.setVisible(true);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
}
The problem that really bothers me is: which one uses memory more efficiently? For one or two dialogs in a software, this is probably not a problem at all. If I need to deals with hundreds of dialog, I need to think about the memory resources that I might consume. According what I have learned so far, calling that method is better than creating MyDialog object, since memory will be released when once the method is processed. However, the object MyObject will be remained in memory if I don't take any action on it. The problem that I am facing is: after a few hours of developing, I found out that I only have a few MB of memory left and I have to quit Eclipse and restart my computer to release the memory.
Therefore:
1) Which one is better?
2) If creating object is better, how can I remove that object from memory if I am not going to use it anymore?
Memory usage is irrelevant here. The difference will be around 20 bytes, that will be garbage collected.
What matters is correct design. An object's constructor is not meant to have side effects like showing a dialog box. The goal of a constructor is to create an object with a specific state, by initializing its fields, in order to use the object later.
Your example doesn't do anything like that. You would be creating a MyDialog instance, and do nothing with it. All the constructor would do is to have a huge side-effect: displaying a dialog box.
Another bad point in this design is that you're using an instance field to store a value that is in fact a constant, not tied to any specifu MyDialog instance, and only relevant to the constructor. There is no reason at all to have such a field.
And finally, your method doesn't respect the Java naming conventions: methods start with a lower-case letter. And it should be named showDialog() instead of makeDialog, since it doesn't create a dialog box, but shows one.
I'm afraid this question is a bit too general to answer, since you're asking about any and all use cases of GUI design.
Let me instead refer to the specific scenario you mention, which is a dialog.
It might come as a surprise, what's considered as a Swing best practice when it comes to dialogs, is somewhere in between a method and a class.
When working with dialogs in Swing, on the one hand, it's considered a best practice to create a new class for the panel that contains all the GUI, and which will become the content pane of the dialog. On the other hand, you should create a method that will create (and possibly also display) the dialog. In that method, you'll do the following:
Create an instance of JDialog (usually, there's no need to subclass)
Create an instance of your custom panel class (e.g. MyDialogContentPaneClass)
Set the panel as the content pane of the dialog
Display and/or return the dialog

I have a JLabel that doesn't want to update

I've looked at other JLabel threads and though similar, a few just don't seem to apply to what I'm experiencing. First, I want to say I am a novice when it comes to Java. Next, I am trying to follow tutorials and demos at the docs.oracle.com site. Now, I can update the label when I type something into a JTextField and there is an ActionListener on that...
But I also have a Menu, and when I select a Menu Item, that Action does not want to update the label.
Questions -
How do I have action listeners on both JTextFields and JMenuItems? Are there two ActionEvent methods or do I use one method and somehow identify each type of action?
If I use the same basic code in the JTextField ActionEvent and JMenuItem ActionEvent, the JLabel updates correctly with the JTextField event but not JMenuItem event. They both use the messageField.setText property. Could the JMenuItem action be doing something to block the setText?
I can easily copy code in here, but it's pretty spaghetti-like at the moment, so if you want to see anything, let me know specifically and I'll post it.
I would appreciate any assistance that anyone would be able to provide.
---edit---
Wow!! Thanks for all of the comments and suggestions.
1 - I know it has to be my code. As I mentioned, I am really just cobbling stuff together from demos and tutorials, and trying to learn Java along the way. I've just never gotten the hang of object oriented....
2 - I do know the individual Listeners are working. I'm using System.out.println to validate, as well as inspecting those labels in debug mode to see they have indeed changed.
3 - I will look at the various links and code posted here and see if I can figure out what's wrong with my code.
Really, thanks again!
---edit---
Here is what I originally had in my createAndShowGUI method....
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Create XML for Photo Gallery");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
CreateGalleryXML window = new CreateGalleryXML();
frame.setJMenuBar(window.createMenuBar());
frame.add(new CreateGalleryXML());
frame.pack();
frame.setVisible(true);
}
Seems like you yourself are doing something wrong, in your code. Without a proper SSCCE it's hard to say what thing you doing wrong, since it works perfectly, using same ActionListener for both JMenuItem and JTextField.
Here is a sample program to match with yours :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class UpdateLabel
{
private JLabel label;
private String labelText;
private ActionListener action = new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
setLabelText((String) ae.getActionCommand());
label.setText(getLabelText());
}
};
private void setLabelText(String text)
{
labelText = text;
}
private String getLabelText()
{
return labelText;
}
private void createAndDisplayGUI()
{
final JFrame frame = new JFrame("Update Label");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationByPlatform(true);
JMenuBar menuBar = new JMenuBar();
JMenu programMenu = new JMenu("Program");
JMenuItem exitMenuItem = new JMenuItem("Exit");
exitMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
frame.dispose();
}
});
JMenu labelMenu = new JMenu("Label");
JMenuItem updateMenuItem = new JMenuItem("Update Label");
updateMenuItem.setActionCommand("Updated by JMenuItem");
updateMenuItem.addActionListener(action);
programMenu.add(exitMenuItem);
labelMenu.add(updateMenuItem);
menuBar.add(programMenu);
menuBar.add(labelMenu);
frame.setJMenuBar(menuBar);
JPanel contentPane = new JPanel();
label = new JLabel("I am the LABEL which will be updated!!");
contentPane.add(label);
JTextField tfield = new JTextField(10);
tfield.setActionCommand("Updated by JTextField");
tfield.addActionListener(action);
frame.add(contentPane, BorderLayout.CENTER);
frame.add(tfield, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new UpdateLabel().createAndDisplayGUI();
}
});
}
}
And here is the output in both the cases :
and
Do check out the main method, might be you had failed to put your code inside EDT - Event Dispatcher Thread, that can lead to such issues too. All updates on the Swing GUI, must be done on the Event Dispatcher Thread.
LATEST EDIT
It seems to me that CreateGalleryXML extends JPanel by the look of it. See at Line 3 of this below code taken from your update, here you are initializing a new Object of CreateGalleryXML inside, when you already had one Object window created at Line 1:
CreateGalleryXML window = new CreateGalleryXML();
frame.setJMenuBar(window.createMenuBar());
frame.add(new CreateGalleryXML());
So try to change the above thingy to this
CreateGalleryXML window = new CreateGalleryXML();
frame.setJMenuBar(window.createMenuBar());
frame.add(window);
and see what happens and Please do revert back again :-)
Use Action to encapsulate common functionality that must be shared by menus and related components. See this example that extends AbstractAction.
Pretty much everything you could need to know is in the Java tutorials. Down the bottom they have demo's on how to do both menu's and text fields. They include source code to look at as well.
There's not much more I can say that they don't say better, but in answer to your questions:
Both, you can have separate listener's on each component, or one that figures out what component is responsible for causing the action event. I would suggest you use separate ones for each thing.
Can't really say with out seeing the code, are you sure that the action on the JMenu is even firing? Does it print something to the console etc if you use System.out.println somewhere?

Categories