Error when extending class - java

I'm pretty new to Java, so I don't know what's going wrong. What I have here is a code that is, when it's executed, supposed to "open" a window once, but whenever I extend the class ColoredWordsExperiment in the ButtonHandler class, the window opens infinitely really quickly which nearly causes my computer to crash everytime. If I leave out the extension then it works fine, but then I won't be able to use the objects from the ColoredWordsExperiment class in the ButtonHandler class... Below you can find the code (I left some unimportant stuff out otherwise it'd become too long).
public class ColoredWordsExperiment {
JFrame frame;
ButtonHandler buttonHandler;
ColoredWordsExperiment(){
frame = new JFrame("Colored Words Experiment");
frame.setSize(1200, 150);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button1 = new JButton("Matching");
label1 = new JLabel("test");
label1.setPreferredSize(new Dimension(90, 40));
JPanel labelContainer = new JPanel();
labelContainer.add(label1);
JPanel buttonContainer = new JPanel();
buttonContainer.add(button1);
frame.add(buttonContainer, BorderLayout.SOUTH);
frame.add(labelContainer, BorderLayout.NORTH);
buttonHandler = new ButtonHandler();
button1.addActionListener(buttonHandler);
}
public static void main(String[] arg) {
new ColoredWordsExperiment();
}
}
-
public class ButtonHandler extends ColoredWordsExperiment implements ActionListener {
#Override
public void actionPerformed(ActionEvent e){
if (e.getActionCommand().equals("Matching")) {
System.out.println("Matching");
label1.setText("Text changed");
}
}
}

There's no reason to extend ColoredWordsExperiment in this case. You should simply be implementing ActionListener.
You're essentially initializing another instance of ColoredWordsExperiment inside itself with a single additional method. This causes your constructor be called again, which causes the GUI window to be recreated.
See Inheritance for more details.
If you want to change a field in ColoredWordsExperiment from your ActionListener implementation, you'll want to pass a reference during construction.
public class ButtonHandler implements ActionListener {
ColoredWordsExperiment coloredWords;
public ButtonHandler(ColoredWordsExperiment coloredWords) {
this.coloredWords = coloredWords;
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Matching")) {
System.out.println("Matching");
coloredWords.label1.setText("Text changed");
}
}
}
In this case, you also have the option of creating an anonymous inner class. With this technique, you can completely get rid of the ButtonHandler class.
Inside ColoredWordsExperiment instead of
buttonHandler = new ButtonHandler();
button1.addActionListener(buttonHandler);
You can use
button1.addActionListener(new ActionListener() {
if (e.getActionCommand().equals("Matching")) {
System.out.println("Matching");
label1.setText("Text changed");
}
});
See Anonymous Classes

In the constructor of the parent class, you are creating one of those ButtonHandlers wich, in turn, runs the constructor code again.
You should not instantiate this class in the constructor (or use a different name for it if you are attempting to use one with the same name)

Okay i can see what tour problem is. Now i don't know why you want to extend it, I'm just trying to answer your question (b/c there is no need to extend an action listener to your main class). when you define the class method you didn't put a public, and then when you put under the main method to run it, it could be confused and make infinite frames. You should change it to this:
public class ColoredWordsExperiment {
JFrame frame;
ButtonHandler buttonHandler;
public ColoredWordsExperiment(){//add the public!
frame = new JFrame("Colored Words Experiment");
frame.setSize(1200, 150);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button1 = new JButton("Matching");
label1 = new JLabel("test");
label1.setPreferredSize(new Dimension(90, 40));
JPanel labelContainer = new JPanel();
labelContainer.add(label1);
JPanel buttonContainer = new JPanel();
buttonContainer.add(button1);
frame.add(buttonContainer, BorderLayout.SOUTH);
frame.add(labelContainer, BorderLayout.NORTH);
buttonHandler = new ButtonHandler();
button1.addActionListener(buttonHandler);
}
public static void main(String[] arg) {
new ColoredWordsExperiment();
}
}
Additionally, it's not a good idea to define your buttonHandler variable as your ButtonHandler class in your Main class but then have your ButtonHandler class extend off your Main class. that could cause a loop. you should either not extend your second class or define your buttonHandler variable in a different way.

It sounds like you need to read up and understand the difference between classes and objects. To help you with that I want to illustrate a problem with the way you were originally thinking about your code with a simplified example:
class A {
int x;
B b = new B();
}
class B extends A {
}
class Main {
public static void main(String args[]) {
A a = new A();
a.x = 42;
a.b.x = 53;
System.out.println(a.x);
System.out.println(a.b.x);
}
}
As expected, the object a in main() has a field named x and we can set it to 42. However, the object b inside of a has its own field named x which is completely unrelated to the field x inside of the object a.
If you can get your head around the concept of objects, you will be well on your way to becoming a good Java programmer.

Related

ActionListener in parent class

I am struggling with the ActionListener in Java in a parent class, I tried a bunch of possible solutions but could not get it work. This here also did not help:
Java actionlistener actionPerformed in different class
The problem is as follows:
Class2 extends Class1, I have a button in Class2. As soon as the button in Class2 is pressed, Class1 should be notified through action listener and perform the event.
I'm struggling to let Class1 know that the event has happened. It looked pretty simple to me, but nevertheless I'm struggling.
Your help will be much apprechiated, thank you!
Parent Class
package test;
//imports removed for better visibility
public class ParentClass extends JPanel implements ActionListener{
JFrame frame;
public void createParentGui() {
frame = new JFrame("Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel mainCard = new JPanel(new CardLayout(20, 20));
ChildClass card1 = new ChildClass();
mainCard.add(card1);
frame.add(mainCard, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Button pressed, action!");
}
}
Child Class
package test;
//imports removed for better visibility
public class ChildClass extends ParentClass {
ActionListener listener = null; //this is probably not right, how to do
//with a local variable when passing it to the parent class?
public Child() {
createGui();
}
private void createGui() {
final JButton b = new JButton("press me");
b.addActionListener(listener);
add(b);
}
}
ChildClass has all of the fields and methods that ParentClass does (in addition to its own unique fields and methods). This is how inheritance works.
So, since ParentClass is an ActionListener, that means that ChildClass is too. More specifically, ChildClass has inherited the public void actionPerformed(ActionEvent e) method of ParentClass.
Therefore, change b.addActionListener(listener); to b.addActionListener(this). (you can also remove the listener field of ChildClass)
The new code will pass "this" ChildClass object to b, which will then call actionPerformed(ActionEvent e) whenever the button is pressed. And since any ChildClass object has the actionPerformed(ActionEvent e) of ParentClass, that means that ParentClass#actionPerformed(ActionEvent) will be called (as you intended).

How to change a JLabel with a button from another class?

I have two classes and a text file database, The JLabel in the first class, let's call it class1 automatically set it self to the number in the database. Now, in class2 I have this little JFrame and a text field and of course a button, the value I put in the text field overwrites the one in the database, but here's the problem. The label in the first class wont update while running, but if I restart it it will show me the value that I want.
How do I update it while the program is running?
I've tried to change the label in the buttonActionperformed in the other class but it gives me a NullPointException every time.
How do I fix this?
THE MAIN CLASS ( JUST THE JFRAME )
package BankrollG;
import java.awt.Graphics;
import javax.swing.JFrame;
public class BGbrain {
BGbody body = new BGbody();
JFrame Frame = new JFrame();
public BGbrain() {
Frame.setSize(body.width, body.height);
Frame.setTitle(body.title);
Frame.setResizable(false);
Frame.setDefaultCloseOperation(Frame.EXIT_ON_CLOSE);
Frame.add(body.panel);
Frame.setLocationRelativeTo(null);
Frame.setFocusable(true);
Frame.setVisible(true);
}
public static void main(String[] args ) {
new BGbrain();
}
}
Then you got the class with the components:
private JLabel bankroll_label
public BGbody(){
panel.setLayout(null);
windowComponents();
}
public void windowComponents() {
// Bankroll database access
try {
FileReader fr = new FileReader("Bankroll.txt");
BufferedReader br = new BufferedReader(fr);
set_bankroll = br.readLine();
} catch(IOException e) {
System.out.println("FEL MED LĂ„SNING AV DATABAS /// BGBODY");
}
}
}
THEN you got the JFrame class that I created with the netbeans function
private void AddcurrencyActionPerformed(java.awt.event.ActionEvent evt) {
String CBR = txt_bankroll.getText();
try {
FileWriter fw = new FileWriter("Bankroll.txt");
PrintWriter pw = new PrintWriter(fw);
pw.println(CBR);
pw.close();
} catch(IOException e) {
System.out.println("FEL MED INSKRIVNINGEN I DATABASEN");
}
}
Now, everything goes as plan, but I can't update my JLabel "bankroll_label" from the button class because it just returns nullpointsexceptions. The data is there, because the JLabel reads from the database but it wont update when changes has been made from the button class. So a getter setter method wont work because the value IS there but it wont update the JLabel.
I hope this made it easier to understand my problem.
It's ALOT more code, that dont have to do with this, I hope I simplified it at least some.
Your question is a specific example of a basic problem in programming in Java -- how to transfer information between classes. There are several ways to do this, one of the most elegant being giving to use a "model" class that holds your program's logic code and key data, having one class change the model's state by changing a text String that it holds. Then using a listener or observer pattern, have the model notify the other class that it has been changed so the other class can extract the new information, its new String from the model. While this is likely the best solution, it may be a bit of overkill and likely is above your current level of coding, so for you, I'm not going to recommend this.
Instead, I'm going to recommend a simpler less elegant solution, that you instead have one class call a setter method of the other to push the new String into it.
One problem we have as volunteer answerers here is that your question is hard to answer because it lacks critical code, making it hard for us to guess why your code is misbehaving, why specifically you're running into a NullPointerException (or NPE) when you try to run it. So all I can do is guess, but guess I will try nevertheless.
For simplicity's sake, let's call one class the, the one that holds the JLabel, the LabelClass and the other class the ButtonTextFieldClass.
One possible reason is that you've got a NullPointerException is because your ButtonTextFieldClass may have a LabelClass variable, but never initialized the variable, something like so:
// this guy is null because it is never initialized
private LabelClass labelClass;
A simple solution could be to try to initialize it like so:
private LabelClass labelClass = new LabelClass();
But this won't work because while it does create and assign a LabelClass instance, it's likely not the LabelClass instance that is visualized in the running GUI.
A better solution is to give the ButtonTextFieldClass a setter method that allows other classes to set the ButtonTextFieldClass with the proper LabelClass instance.
e.g.,
public void setLabelClass(LabelClass labelClass) {
this.labelClass = labelClass;
}
This way the code that sets up both classes can pass the visualized LabelClass to the first class, and it can call methods on it.
A simple example of LabelClass could look like so:
class LabelClass extends JPanel {
private JLabel label = new JLabel("");
public LabelClass() {
setBorder(BorderFactory.createTitledBorder("Label Panel"));
add(label);
}
public void setLabelText(String text) {
label.setText(text);
}
}
I have it extend JPanel because this way it gives me the freedom of placing it into a JFrame or JDialog or other JPanel as I see fit. Note that I've made the JLabel private and have given the class a public setter method, setLabelText(String text), that allows outside classes the ability to set the JLabel's text.
The ButtonTextFieldClass could look something like:
class ButtonTextFieldClass extends JPanel {
private JTextField textField = new JTextField(10);
private JButton button = new JButton(new ButtonAction("Send Text"));
private LabelClass labelClass;
public ButtonTextFieldClass() {
setBorder(BorderFactory.createTitledBorder("Button TextField Panel"));
add(textField);
add(button);
}
// here we allow other classes to set instances of our LabelClass
public void setLabelClass(LabelClass labelClass) {
this.labelClass = labelClass;
}
// ....
I've also given the button an AbstractAction in place of an ActionListener since it is like a super ActionListener on steroids. Inside of it, I'd get the text from the JTextField and then call the LabelClass's setter method (if the variable is not null) to set the label's text:
public void actionPerformed(ActionEvent e) {
String text = textField.getText();
if (labelClass != null) {
labelClass.setLabelText(text);
}
}
Then to set everything up, in another class I'd create instances of both LabelClass and ButtonTextFieldClass, and then "hook them up" by calling the setter method:
LabelClass labelClass = new LabelClass();
ButtonTextFieldClass buttonTextFieldClass = new ButtonTextFieldClass();
buttonTextFieldClass.setLabelClass(labelClass); // set our reference
The whole thing could look like so:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class TransferData {
private static void createAndShowGui() {
LabelClass labelClass = new LabelClass();
ButtonTextFieldClass buttonTextFieldClass = new ButtonTextFieldClass();
buttonTextFieldClass.setLabelClass(labelClass); // set our reference
JPanel mainPanel = new JPanel(new GridLayout(0, 1));
mainPanel.add(buttonTextFieldClass);
mainPanel.add(labelClass);
JFrame frame = new JFrame("TransferData");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class LabelClass extends JPanel {
private JLabel label = new JLabel("");
public LabelClass() {
setBorder(BorderFactory.createTitledBorder("Label Panel"));
add(label);
}
public void setLabelText(String text) {
label.setText(text);
}
}
class ButtonTextFieldClass extends JPanel {
private JTextField textField = new JTextField(10);
private JButton button = new JButton(new ButtonAction("Send Text"));
// one possible solution -- give this class a variable
// of the LabelClass -- but don't initialize the variable
// here, but rather do it in a setter
private LabelClass labelClass;
public ButtonTextFieldClass() {
setBorder(BorderFactory.createTitledBorder("Button TextField Panel"));
add(textField);
add(button);
}
// here we allow other classes to set instances of our LabelClass
public void setLabelClass(LabelClass labelClass) {
this.labelClass = labelClass;
}
// an AbstractAction is like a "super" ActionListener
private class ButtonAction extends AbstractAction {
public ButtonAction(String name) {
super(name); // set the button's text and actionCommand
int mnemonic = (int) name.charAt(0); // get first char
putValue(MNEMONIC_KEY, mnemonic); // set mnemonic
}
#Override
public void actionPerformed(ActionEvent e) {
String text = textField.getText();
if (labelClass != null) {
labelClass.setLabelText(text);
}
}
}
}
For simplicity's sake, I've displayed both JPanels within the same GUI, but it could work just as well if one JPanel were in one JFrame and the other within a JDialog.

How can a component on a panel alert other components outside the panel when a value changes?

I am writing a Swing program in which all panels are declared as separate classes and a JFrame that holds the panels. The input panel has a subpanel that has a number of buttons on it. When when one of those buttons is clicked and a value is calculated, I would like to pass that value back to the JFrame holding the input panel, so that the value can be used in a number of other calculations.
I know that I can just use the JFrame ActionListener to be directly responsible for the buttons on the sub panel, but that seems to violate either portability, encapsulation, or both. I want the sub panel to be able to work on its own and let the JFrame using it be aware when a certain value changes.
I would prefer to keep the value as a primitive, but if only an object works, I can go with that.
The program has already been written as one class and it worked fine, but a beast to edit. When I was done, I realized that it was a mistake not to break it down into six panels each as their own class. A lesson for future projects. So, I am rewriting it and running into the problem that the JFrame is dependent on knowing when one of its panels (actually a panel on that panel) recalculates a value. The value calculation isn't a problem, but the JFrame needs to know when it changes.
I hope that is clear. It seemed pretty clear to me! :-)
The following code is a simplification of what I'm trying to do.
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
public class ChangeState extends JFrame {
// This is to receive the value when changed in ButtonPanel
private JTextField answer = new JTextField(5);
private InputPanel inputPanel = new InputPanel();
public ChangeState() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(inputPanel, BorderLayout.CENTER);
add(answer, BorderLayout.SOUTH);
pack();
setVisible(true);
}
public static void main(String[] args) {
ChangeState mf = new ChangeState();
}
}
class InputPanel extends JPanel {
private ButtonPanel buttonPanel = new ButtonPanel();
InputPanel() {
add(buttonPanel);
}
}
class ButtonPanel extends JPanel implements ActionListener {
private int value;
JButton b1 = new JButton("Value *2");
JButton b2 = new JButton("Value *3");
public ButtonPanel() {
value = 1;
b1.addActionListener(this);
add(b1);
b2.addActionListener(this);
add(b2);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == b1) {
value *= 2;
}
if (e.getSource() == b2) {
value *= 3;
}
System.out.println("Value: " + value);
/*
* How do I let ChangeState.answer know VALUE has changed and needs
* to be updated?
*/
}
}
You can make ChangeState an observer of your ButtonPanel, by being an ActionListener listening to the ButtonPanel, or by being some other custom observer that's more abstract which you can create.
I'd recommend a more abstract custom observer when the class observing is not another GUI class with only one event, but in this case since it is perhaps you can just use an ActionListener (on the other hand, ChangeState is not much of a GUI class, you could have made it be composed of a JFrame).
If you have more than one event that could be passed through that ActionListener, you will probably then need to make ChangeState implement a more abstract observer implementation so you can better distinguish between events. Otherwise you'll need to have ChangeState know the name of some of the different GUI components that created the event's, or have a reference to them which would not be that great design wise.

Implementing and calling JFrame/ActionListener class from main

I'm trying to get my program to launch a gui that gathers information before the actual programs starts. In main I try to call the JFrame which should then run until the start button is pressed and then the main program should launch. Everything seems to be correct except for the base class of the initializeLauncher. Thanks!
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class InitializeLauncher implements ActionListener {
InitializeLauncher() {
JFrame frame = new JFrame("launcherClient");
Container c = frame.getContentPane();
Dimension d = new Dimension(700,400);
c.setPreferredSize(d);
JButton startButton = new JButton("Start");
JPanel pane = new JPanel();
startButton.addActionListener(this);
pane.add(startButton);
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
}
public void buttonClicked(ActionEvent e)
{
ApplicationDeploy displayExample = new ApplicationDeploy();
displayExample.initializeGameClient();
}
}
...and then in main I call this:
InitializeLauncher launcher = new InitializeLauncher();
launcher.InitializeLauncher();
By making your class abstract, you're fixing the wrong thing. Instead you should give your class the missing method, public void actionPerformed(ActionEvent e) {...}
The basic rule here is, if you state that your class is going to implement an interface, here the ActionListener interface, then the class must implement all of the methods of the interface.
#Override
public void actionPerformed(ActionEvent e) {
// ... your code that should occur when the button is pressed goes here
}
Note that your buttonClicked(...) method will do nothing useful for you. Likely you'll want to get rid of that method and put its code into the actionPerformed method.
As an aside, I often use a JOptionPane for the functionality that you're using a JFrame for.

Actionlisteners in constructor

I am trying to create NewCard class, with implements a frame. How can I add Actionlisteners to elements in constructor of NewCard class? I can't put Actionlistener into constructor, and when I put it outside, element "field" is invisible for saveButtonListener block..
Second question: class Record in try block throws two exceptions, why try block generate error?
package Interface;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import Engine.*;
class NewCard extends JFrame
{
NewCard()
{
JFrame Card = new JFrame();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setTitle("New Card");
setSize(340, 580);
setVisible(true);
Container contentPane = getContentPane();
contentPane.setLayout(null);
// Field
JTextField field = new JTextField();
contentPane.add(field);
field.setBounds(110,15,200,25);
// Button:
JButton saveButton = new JButton("Save");
powZawartosci.add(saveButton);
saveButton.setBounds(95,495,150,25);
saveButtonListener listener1 = new saveButtonListener();
saveButton.addActionListener(listener1);
}
private class saveButtonListener implements ActionListener
{
try
{
#Override
public void actionPerformed(ActionEvent event)
{
new Record(field.getText());
}
}
catch(IOException e)
{
System.out.println("IOException");
}
catch(SQLException e)
{
System.out.println("SQLException");
}
finally
{
}
}
}
You could put your action listener inside constructor like this:
final JTextField field = new JTextField();
...
saveButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
new Record(field.getText());
}
});
Pass field to the saveButtonListener by creating an appropriate constructor for the listener.
Or use an anonymous listener, as shown in Mersenne's answer.
regarding:
I can't put Actionlistener into constructor, ...
Who says you can't? Simply add the ActionListener...
JButton saveButton = new JButton("Save");
saveButton.addActionListener(new SaveButtonListener()); // capitalize class names
Or you can use anonymous inner classes, or even better, use AbstractActions.
Edit 1:
Regarding:
Second question: class Record in try block throws two exceptions, why try block generate error?
If you have a question about an exception, it makes lots of sense to show the exception.
Edit 2
Regarding:
class NewCard extends JFrame
{
NewCard()
{
JFrame Card = new JFrame();
Why have the class extend JFrame and create a JFrame inside the class that is never used?Best to not have the class extend JFrame but rather to create the JFrame when needed.

Categories