public class MainFrame extends JFrame {
MainFrame()
{
JButton zeroButton = new JButton("0");
add(zeroButton);
Handler handler = new Handler();
zeroButton.addActionListener(handler);
}
private class Handler implements ActionListener
{
public void actionPerformed(ActionEvent e) {
if (e.getSource() == **zeroButton**)
JOptionPane.showMessageDialog(null, "hello there!");
}
}
}
The code has an error which underlines what I marked as bold in the code.
here is the error message: "cannot find symbol"
I thought I can access outer class modifiers from the inner class, but it doesn't work!
zeroButton is not a member of outer class (MainFrame), it's a local variable in MainFrame constructor.
Try something like this
public class MainFrame extends JFrame {
private final JButton zeroButton;
MainFrame() {
zeroButton = new JButton("0");
PS I'm also not sure if you're supposed to compare controls with ==. (never used swing)
You are unable to access it because the button is a local variable in another method.
You have two options:
1. Make the button in a instance variable (a class level variable). See Nikita's answer.
Have the handler as an anonymous implementation, in the constructor:
MainFrame() {
final JButton zeroButton = new JButton("0");
add(zeroButton);
Handler handler = new Handler();
zeroButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
if (e.getSource() == **zeroButton**)
JOptionPane.showMessageDialog(null, "hello there!");
}
});
}
In this case, the variable should be final because only final local variables are accessible to inner classes.
Related
I'm working through the book "Head first Java" and started chapter 12.
When I tried to create the method changeButtonText() I cannot access any of the class methods from button.
Why is this? What have I done wrong in this code?
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JButton button = new JButton("Click Me");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
}
public void changeButtonText(){
button.setText("I've been clicked");
}
}
The reason you cannot access the variable is because of its scope (tutorial about the scope of variables: https://www.baeldung.com/java-variable-scope).
You declare the variable JButton button in the main method, therefore it is not accessible anywhere outside of it, even in a method that main calls itself.
To make changeButtonText aware that the button variable exists, you have to pass it as parameters of this method:
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JButton button = new JButton("Click Me");
changeButtonText(button);
}
public static void changeButtonText(JButton button){
button.setText("I've been clicked");
}
}
I also added the static keyword in front of the changeButtonText method, because main is also a static method. Check this link for example to have more details about the difference: https://www.geeksforgeeks.org/static-methods-vs-instance-methods-java/
Surely this is a problem of scope and it can be overcome with defining variable at global level and initialize wherever you required. something like -
public class Main {
private static JButton button;
public static void main(String[] args) {
JFrame frame = new JFrame();
button = new JButton("Click Me");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
Main.changeButtonText();
}
public static void changeButtonText(){
button.setText("I've been clicked");
}
}
There's a couple of things that is wrong with your code and concepts that you seem to not fully grasp. These concepts are this. and scopes.
public void changeButtonText(){
button.setText("I've been clicked");
}
First we need to talk about scopes. A scope is what's declared between { /* here */ } i.e. two curly brackets. From within a scope you can access and declare both local and global objects, variables, members etc.
So here's an example using local scope (what you did):
public void myFunction(){
JButton button = new Jbutton();
}
If I then go ahead and try to access the button outside of the scope, it wont know what button is. Since it's not declared within that local scope.
public void myFunction(){
JButton button = new Jbutton();
}
public static void main(String[] args) {
button.setText("hello"); //error
// compiler thinks: what's `button` referring to here? its not declared in this scope.
}
Global class scope:
JButton button = new JButton(); //declaring button in global class scope
public void myFunction(){
this.button.setText("hello"); // will access the globally declared variable, object, member called `button`.
//^ notice the usage of `this`.
//`this` will look outside of the local functions scope for publically declared class-members.
}
To fix your particular problem, you could either make your function static and pass in a JButton object, or you could declare your button in the global class scope like this:
public class Main {
JButton button = new JButton("Click Me"); //global class scope
public static void main(String[] args) { //local scope of main start {
Main myClass = new Main(); //instantiate class
myClass.changeButtonText(); //access class-member function
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(this.button); // this.
frame.setSize(300,300);
frame.setVisible(true);
} // local scope of main ends }
public void changeButtonText(){
this.button.setText("I've been clicked"); //access global class members using `this` keyword
}
}
Ok, here is my problem. Class B is a class that build a GUI ,which has a textField and button. class A has an instance of class B.Now I enter some value in the textfield, when I click the button, in class A I want to print out the value I just enter in the textfield, how can I achieve that?
Code below may better explain what I want to achieve:
public class A
{
B myB = new B();
(when the JButton was clicked,
how can I get the new textfield value here?)
}
public class B
{
JLabel myLabel;
JButton myButton;
public B()
{
getContentPane().setLayout(null);
myLabel = new JLabel();
myLabel.setLocation(0,0);
myLabel.setSize(100,30);
myLabel.setBackground( new Color(-6710887) );
myLabel.setText("");
getContentPane().add(myLabel);
myButton = new JButton();
myButton.setLocation(0,50);
myButton.setSize(100,30);
myButton.setBackground( new Color(-16737895) );
myButton.setText("Submit");
getContentPane().add(myButton);
myButton.addActionListener(this);
setSize(400,400);
setVisible(true);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e)
{
(how can I pass this "myLabel.getText()" value to class A when
this action performed?)
}
}
Can anybody help me finish this little program? Thanks in advance!
You need to expose the value in text field with a method in class B. Then class A can call that method. What it actually sounds like though is that class A (or something else) should be a ActionListener for your button.
However, a bigger problem is that you don't have a text field you just have a label in class B. This code is a good reason why you shouldn't use a GUI builder, especially when learning Swing.
Some reading:
http://docs.oracle.com/javase/tutorial/uiswing/components/textfield.html
http://docs.oracle.com/javase/tutorial/uiswing/events/
I often make an "App" class that ties all my GUI-builder-built components together. Any GUI builder worth anything lets you add getters to the generated source code. Add some getters to the GUI-built components to retrieve key elements of the GUI, then let the App class use the getters to interact with the components as necessary. This won't win any MVC/MVVM/MVP design awards, but it gets the job done, which ought to count for something.
public class App {
private B _b;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
App app = new App();
app.run();
}
});
}
void run() {
_b = new B();
_b.getMainButton().addActionListener(new MainButtonListener());
_b.setVisible(true);
}
private void handleMainButtonClicked() {
String mainText = _b.getMainTextArea().getText();
System.out.println("Button clicked; main text = " + mainText);
}
public class MainButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
handleMainButtonClicked();
}
}
}
public class B extends JFrame {
private JPanel _contentPane;
private JTextArea _jTextArea;
private JButton _jButton;
public B() {
initComponents();
}
private void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
_contentPane = new JPanel();
setContentPane(_contentPane);
_jTextArea = new JTextArea();
_contentPane.add(_jTextArea, BorderLayout.CENTER);
_jButton = new JButton("My Button");
_contentPane.add(_jButton, BorderLayout.SOUTH);
}
public JButton getMainButton() {
return _jButton;
}
public JTextComponent getMainTextArea() {
return _jTextArea;
}
}
This seems like a very simple problem, but I'm having a lot of trouble figuring out how to deal with it.
Sample Scenario:
final int number = 0;
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setSize(400, 400);
final JTextArea text = new JTextArea();
frame.add(text, BorderLayout.NORTH);
JButton button = new JButton(number + "");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
number++; // Error is on this line
text.setText(number + "");
}});
frame.add(button, BorderLayout.SOUTH);
I really have no idea where to go.
If you declared number as final, you cannot modified its value. You must remove the final modificator.
Then, you can access to that variable via:
public class Scenario {
private int number;
public Scenario() {
JButton button = new JButton(number + "");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Scenario.this.number++;
text.setText(Scenario.this.number + "");
}
});
}
}
The notation "ClassName.this" allow you to access to the object of a class where you are in.
Keep atention in when you use "number" first time, -new JButton(number)-, you can access to number directly, because you are in Scenario scope. But when you use it inside your ActionListener, you are in your ActionListener scope instead of Scenario scope. That is why you cannot see the variable "number" directly inside of your action listener and you have to access to the instance of Scenario where you are in. This can be done by Scenario.this
The fastest solution would be to declare number as static, and reference it using the name of your class.
Alternatively you can make a class that implements ActionListener, and pass number and text into it's constructor.
I'm having troubles with the .getText() command trying to retrieve data from a JTextField. I've been looking for the solution but couldn't find it. Is there anybody who can see what I'm doing wrong?
What I'm trying is to make is a bouncing ball which parameters such as elasticity, gravity and initial speed can be changed.
When trying to compile the following message appears:
ERROR in BBPanel.java (at line 88) String gravityIn = gravity_input.getText();
gravity_input cannot be resolved
Thanks!
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/////////////////////////////////////////////////////////////////// BBPanel
class BBPanel extends JPanel {
BallInBox n_bb; // The bouncing ball panel
//========================================================== constructor
/** Creates a panel with the controls and bouncing ball display. */
BBPanel() {
//... Create components
n_bb = new BallInBox();
JButton startButton = new JButton("Start");
JButton stopButton = new JButton("Stop");
JButton resetButton = new JButton("Reset");
JButton gveButton = new JButton("GetData");
JLabel gravityLbl = new JLabel (" set gravity " );
JLabel velocity_yLbl = new JLabel (" set initial y-dir speed " );
JLabel elasticityLbl = new JLabel (" set elasticity [0-100%]" );
JTextField gravity_input = new JTextField(20);
JTextField velocity_y_input = new JTextField(20);
JTextField elasticity_input = new JTextField(20);
//... Add Listeners
startButton.addActionListener(new StartAction() );
stopButton.addActionListener(new StopAction() );
resetButton.addActionListener(new ResetAction() );
gveButton.addActionListener(new DataAction() );
//... Layout inner panel with three buttons horizontally
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.add(startButton);
buttonPanel.add(stopButton);
buttonPanel.add(resetButton);
//
JPanel variablePanel = new JPanel();
variablePanel.setLayout(new GridLayout(0,2));
variablePanel.add(gravityLbl);
variablePanel.add(gravity_input);
variablePanel.add(velocity_yLbl);
variablePanel.add(velocity_y_input);
variablePanel.add(elasticityLbl);
variablePanel.add(elasticity_input);
variablePanel.add(gveButton);
//... Layout outer panel with button panel above bouncing ball
this.setLayout(new BorderLayout());
this.add(buttonPanel , BorderLayout.NORTH);
this.add(n_bb , BorderLayout.CENTER);
this.add(variablePanel, BorderLayout.EAST);
}//end constructor
////////////////////////////////////// inner listener class StartAction
class StartAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
n_bb.setAnimation(true);
}
}
//////////////////////////////////////// inner listener class StopAction
class StopAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
n_bb.setAnimation(false);
}
}
///////////////////////////////////////// inner listener class ResetAction
class ResetAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
n_bb.resetAnimation(true);
}
}
////////////////////////////////////////// inner listener class GravityAction
class DataAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
String gravityIn = gravity_input.getText();
//System.out.println(gravityIn);
n_bb.setData(true);
}
}
}//endclass BBPanel
The variable gravity_input is only defined locally in the scope of the constructor of BBPanel. You need to make it a class member variable if you wish to make it visible to your DataAction class.
public class BBPanel extends JPanel {
private JTextField gravity_input; // to be initialized...
...
Side note:
Java uses camel-case which would make gravity_input gravityInput.
Your problem is one of scope. The gravity_input is declared inside of a constructor or method. To be visible outside of these blocks, it should be declared in the class similar to how you declare the n_bb field.
Your variable gravity_input should be declared as an instance variable (outside the constructor), for it to be later accessed.
The way you're defining it, the name (not the text field) gravity_input dies as soon as the constructor code terminates (reaches closing brace }).
pass parameters as follows into constructor of DataAction:
class DataAction implements ActionListener {
final JTextField textField;
final BallInBox bb;
public DataAction(final JTextField textField, final BallInBox bb) {
this.textField = textField;
this.bb = bb;
}
public void actionPerformed(ActionEvent e) {
String gravityIn = textField.getText();
//System.out.println(gravityIn);
bb.setData(true);
}
}
and use it like this:
gveButton.addActionListener(new DataAction(gravity_input, n_bb));
Then refactor your other actions in a similar way.
The problem is, that your class DataAction doesn't know about your gravity_input textfield.
The textfield gravity_input is only visible to the class BBPanel. To make it visible to the class DataAction, you have to pass the textfield to that class like this:
class DataAction implements ActionListener {
private JTextField gravity_input;
public DataAction(JTextField txtField) {
gravity_input = txtField;
}
public void actionPerformed(ActionEvent e) {
String gravityIn = gravity_input.getText();
//System.out.println(gravityIn);
n_bb.setData(true);
}
}
and change your BBPanel constructor to this:
BBPanel() {
//... omitted
gveButton.addActionListener(new DataAction(gravity_input));
//... omitted
}
Basically what I want to do is get a start button to initiate a method running in another class and acting on another object.
My code for the listener:
button1a.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent event) {
// Figure out how to make this work
//sim.runCastleCrash();
}
} );
My code for the other class:
public static void main(String[] args) {
CastleCrash sim;
sim = new CastleCrash();
}
and
public void runCastleCrash() {
System.out.println("Castle Crash is beginning...");
//Other method parts here to be added
}
I get the feeling this can't be too hard, but I'm missing a piece.
One way to reference things in an anonymous class is using the final keyword:
public static void main(String[] args) {
final Object thingIWantToUse = "Hello";
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
System.out.println(thingIWantToUse);
}
});
JFrame frame = new JFrame();
frame.setLayout(new FlowLayout());
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
Alternatively, you can access members (variables or methods) of an enclosing type:
public class ActionListenerDemo2 {
private final JFrame frame = new JFrame();
private Object thingIWantToUse = "Hello";
public ActionListenerDemo2() {
JButton button = new JButton("Click");
button.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
thingIWantToUse = "Goodbye";
System.out.println(thingIWantToUse);
}
});
frame.setLayout(new FlowLayout());
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new ActionListenerDemo2().frame.setVisible(true);
}
}
I've had the same problem like you and this is how i solved it.
You can either make your object final (final CastleCrash sim = new CastleCrash();), but i didn't want to do that, or you can make something like a setter method to run the method in your other class:
My code for the listener class:
button1a.addActionListener(new ActionListener()
{
public void actionPerformed (ActionEvent event)
{
//How to make this work ?
//Like this:
runCC();
}
});
public void runCC()
{
CastleCrash sim = new CastleCrash();
sim.runCastleCrash();
}
My code for the other class:
public void runCastleCrash()
{
System.out.println("Castle Crash is beginning...");
//Other method parts here to be added
}
Hope this is helpful, good luck ! :)
McDowell already answers practically with good examples on how to access variables from event listeners (or anonymous inner classes in general). There is however a more general Sun resource on Event Listeners in Swing that is canonical and a good overview of all the caveats to take into account when writing them.
Somehow you need a reference to your CastleCrash object available to call from your actionListener.
You probably want to subclass JFrame, or whatever is containing your JButton such that it has your both your main method and a CastleCrash property that can then be referenced from your anonymous inner class Actionlistener.
BUT - be careful, you look like you are calling what will be a long running method from within the GUI event thread (where the action listener will called). This is generally a bad idea, you will case your GUI to become unresponsive.
See http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html especially the bit on SwingWorker class for ideas on how to avoid that problem.