Java notSerializableExceptionError - java

When trying to save an arraylist of my class Click, I get this error: java.io.NotSerializableException:javax.swing.text.DefaultHighlighter$LayeredHighlightInfo
on this line of code: os.writeObject(saveList);. Even though I made my Click class implement serializable. Does anyone know the cause of this?
Here is my save Method:
public static void saveArray(ArrayList<Click> saveList) {
JFileChooser c = new JFileChooser();
c.showSaveDialog(new JFrame());
File f = c.getSelectedFile();
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(
f.getAbsolutePath()));
os.writeObject(saveList);
} catch (IOException e1) {
e1.printStackTrace();
}
}
And here is my Click class:
public static class Click implements Serializable {
JTextField xClickField;
JTextField yClickField;
JTextField clickIntervalField;
JTextField repeatTimesField;
boolean isLeft;
Integer clickX;
Integer clickY;
Integer clickInterval;
Integer clickTimes;
public Click(boolean left){
xClickField = new JTextField();
yClickField = new JTextField();
clickIntervalField = new JTextField();
repeatTimesField = new JTextField();
clickX = 0;
clickY = 0;
clickInterval = 0;
clickTimes = 0;
isLeft = left;
addToJPanel();
}
public void addToJPanel() {
xClickField.setText(clickX.toString());
yClickField.setText(clickY.toString());
clickIntervalField.setText(clickInterval.toString());
repeatTimesField.setText(clickTimes.toString());
panel.add(xClickField);
panel.add(yClickField);
panel.add(clickIntervalField);
panel.add(repeatTimesField);
frame.setVisible(false);
frame.setVisible(true);
}
public void removeFromJPanel() {
panel.remove(xClickField);
panel.remove(yClickField);
panel.remove(clickIntervalField);
panel.remove(repeatTimesField);
frame.setVisible(false);
frame.setVisible(true);
}
}
By the way I took out a chunk of code from the Click class. So if you think that the error could be in that portion of the code, I will gladly add it in.
Thanks in advance!

Implementing Serializable is not sufficient to make an object serializable. For example, a Socket is not serializable: it doesn't make sense to serialize a socket. So, if you have a Foo class that has a field of type Socket and that implements Serializable, how do you intend to serialize a Foo instance. It won't work. All the fields of a serializable object msut also be serializable, recursively.
And, as Hovercraft says in his comment, you should serialize data, not swing components.

You're serializing JTextFields and other Swing components which is a waste of time and resources and is completely unnecessary. You should be serializing the state of your GUI, the data held by the class's fields. If you understand MVC, you should be serializing the model, not the view. If you don't understand MVC, Google it or read this article and learn the key concepts as they are key to creating GUI programs in any language.
Also, for my money, I'd use JAXB or some other XML-based tool to serialize your data as it is saved in text format and thus understandable when read.
Example of separating GUI from model and using a property change listener to listen and respond to property changes:
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class SimpleClickEg {
private static void createAndShowGui() {
SimpleClickPanel clickPanel = new SimpleClickPanel();
JFrame frame = new JFrame("SimpleClickEg");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(clickPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class SimpleClickPanel extends JPanel {
private static final int PREF_WIDTH = 800;
private static final int PREF_HIEGHT = 600;
private JTextField clickCountField = new JTextField(5);
private JTextField clickXField = new JTextField(5);
private JTextField clickYField = new JTextField(5);
private SimpleClick click = new SimpleClick();
public SimpleClickPanel() {
add(new JLabel("Click X:"));
add(clickXField);
add(new JLabel("Click Y:"));
add(clickYField);
add(new JLabel("Click Count:"));
add(clickCountField);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
click.setClickPoint(e.getPoint());
}
});
click.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (SimpleClick.CLICK_COUNT.equals(evt.getPropertyName())) {
clickCountField.setText(String.valueOf(click.getClickCount()));
} else if (SimpleClick.CLICK_X.equals(evt.getPropertyName())) {
clickXField.setText(String.valueOf(click.getClickX()));
} else if (SimpleClick.CLICK_Y.equals(evt.getPropertyName())) {
clickYField.setText(String.valueOf(click.getClickY()));
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_WIDTH, PREF_HIEGHT);
}
public SimpleClick getClick() {
return click;
}
}
class SimpleClick implements Serializable {
private static final long serialVersionUID = 1L;
public static final String CLICK_COUNT = "click count";
public static final String CLICK_X = "click x";
public static final String CLICK_Y = "click y";
private int clickCount;
private int clickX;
private int clickY;
private transient SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
this);
public int getClickCount() {
return clickCount;
}
public void setClickCount(int clickCount) {
Integer oldValue = this.clickCount;
Integer newValue = clickCount;
this.clickCount = newValue;
spcSupport.firePropertyChange(CLICK_COUNT, oldValue, newValue);
}
public void incrementClickCount() {
setClickCount(getClickCount() + 1);
}
public void setClickPoint(Point p) {
setClickX(p.x);
setClickY(p.y);
incrementClickCount();
}
public int getClickX() {
return clickX;
}
public void setClickX(int clickX) {
Integer oldValue = this.clickX;
Integer newValue = clickX;
this.clickX = newValue;
spcSupport.firePropertyChange(CLICK_X, oldValue, newValue);
}
public int getClickY() {
return clickY;
}
public void setClickY(int clickY) {
Integer oldValue = this.clickY;
Integer newValue = clickY;
this.clickY = newValue;
spcSupport.firePropertyChange(CLICK_Y, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
spcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
spcSupport.removePropertyChangeListener(listener);
}
}

As you can see the error clearly states that javax.swing.text.DefaultHighlighter is not serializable.
Now this class is used by composition inside the JTextField, which is a GUI component and it is not meant to be serialized. From your code it seems that you don't need to serialize the fields themselves, so just mark them as transient and you are done.
As a side note: it is always good to split what is your data from what is your GUI so that you can easily serialize just data and foget about anything concerning the GUI. This helps in general, not just in serialization, to preserve encapsulation and use OOP as it is meant to be used.

The problem is that your Click class has references to JTextField instances, and these (presumably) have references to some Swing class called DefaultHighlighter.LayeredHighlightInfo ... and that is not serializable.
You probably need to declare the 4 JTextField variables as transient. As a general rule, Java GUI classes such as Swing components are not effectively serializable.

Related

How to pass a variable between JPanel classes?

I have a JTabbedPane with two JPanels that need to stay in seperate classes. In PageOne, I want to be able to increment MyInteger by clicking the add button, and I then want to be able to print that integer in PageTwo by clicking the button there. It prints the correct value in PageOne, but prints 0 when I pass it to the PageTwo class and print it there.
How can I pass the value in such a way that it prints the correct value when clicking the button in both JPanels? I figure it has something to do with how I inherit from PageOne, but couldn't find a way of changing it on SO that solved my problem.
Main class:
import javax.swing.*;
public class MyJFrame {
PageOne pageOne;
PageTwo pageTwo;
public MyJFrame() {
JFrame f = new JFrame();
pageOne = new PageOne();
pageTwo = new PageTwo();
JTabbedPane jTabbedPane = new JTabbedPane();
jTabbedPane.addTab("Page One", pageOne);
jTabbedPane.addTab("Page Two", pageTwo);
f.add(jTabbedPane);
f.setSize(200,120);
f.setVisible(true);
}
public static void main(String[] args) throws InterruptedException {
new MyJFrame();
}
}
JPanel One:
import javax.swing.*;
public class PageOne extends JPanel {
public Integer myInteger = 0;
public JButton add;
public PageOne() {
add = new JButton();
add.setText("Increment number");
add(add);
add.addActionListener(actionEvent -> {
myInteger++;
printOne();
});
}
public void printOne() {
System.out.println("Page One:" + myInteger);
}
}
JPanel Two:
import javax.swing.*;
public class PageTwo extends JPanel {
PageOne pageOneRef = new PageOne();
public JButton button;
public PageTwo() {
JPanel panel = new JPanel();
button = new JButton("Click me");
panel.add(button);
add(panel);
button.addActionListener(e -> printTwo());
}
public void printTwo() {
System.out.println("Page Two:" + pageOneRef.myInteger);
}
}
The basic answer is, you need some kind of "container" which can be shared between the two components. This is commonly achieved through the use of a "model" of some kind.
See:
Model-View-Controller
Observer Pattern
Writing Event Listeners
for an overview of the concepts presented below
Runnable example
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
DefaultIntegerModel model = new DefaultIntegerModel();
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Page One", new PageOne(model));
tabbedPane.addTab("Page Two", new PageTwo(model));
frame.add(tabbedPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface IntegerModel {
public interface Observer {
public void valueDidChange(IntegerModel source, int value);
}
public int getValue();
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
}
public interface MutableIntegerModel extends IntegerModel {
public void setValue(int value);
}
public class DefaultIntegerModel implements MutableIntegerModel {
private int value;
private List<Observer> observers;
public DefaultIntegerModel() {
this(0);
}
public DefaultIntegerModel(int value) {
this.value = value;
observers = new ArrayList<Observer>(8);
}
#Override
public void setValue(int value) {
this.value = value;
fireValueDidChange(value);
}
#Override
public int getValue() {
return value;
}
#Override
public void addObserver(Observer observer) {
observers.add(observer);
}
#Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
protected void fireValueDidChange(int value) {
for (Observer observer : observers) {
observer.valueDidChange(this, value);
}
}
}
public class PageOne extends JPanel {
public JButton add;
private MutableIntegerModel model;
public PageOne(MutableIntegerModel model) {
this.model = model;
add = new JButton();
add.setText("Increment number");
add(add);
add.addActionListener(actionEvent -> {
model.setValue(model.getValue() + 1);
printOne();
});
}
public void printOne() {
System.out.println("Page One:" + model.getValue());
}
}
public class PageTwo extends JPanel {
private JButton button;
private JLabel label;
private IntegerModel model;
public PageTwo(IntegerModel model) {
this.model = model;
model.addObserver(new IntegerModel.Observer() {
#Override
public void valueDidChange(IntegerModel source, int value) {
System.out.println("Page two value did change to " + value);
label.setText(Integer.toString(model.getValue()));
}
});
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
label = new JLabel(Integer.toString(model.getValue()));
add(label, gbc);
button = new JButton("Click me");
button.addActionListener(e -> printTwo());
add(button, gbc);
}
public void printTwo() {
System.out.println("Page Two:" + model.getValue());
}
}
}
But why are there two models
Stop for a second and think about the responsibilities of each component.
PageOne want's to update the model, in order to do so, it also needs to know the value of the model. The model makes no assumption about "how" the consumer of this model will do that (so I didn't provide a increment method), it just allows the consumer to set the value it wants
PageTwo just wants to display the value (and be notified when some change occurs), so it doesn't need a mutable version of the model.
This restricts what consumers maybe able to do to the model rather the exposing functionality to parties which don't need it (and might be tempted to abuse it)
This is a demonstration and your needs may differ, but I'm bit of a scrooge when I design these kinds of things, I need the consumers to prove to me that they need functionality, rather then "assuming" what functionality they "might" require 😉
This is a practice known is "information hiding", which is supported by Polymorphism in OO languages

Java Observer/Observable update

I've tried to apply the Observable/Observer pattern but there is something wrong with my code when I try to change a the textfield of a JTextPane.
I've got 3 classes, Play, Controller and SecondWindow here are a sample of their code.
public class Play() {
Controller c = new Controller();
SecondWindow sw = new SecondWindow();
c.addObserver(sw)
c.setText("blabla");
}
My class Controller:
public class Controller extends Observable(){
private String text ="";
private static Controller getInstance() {
if (instance == null) {
instance = new Controller();
}
return instance;
}
public void setText(String s) {
text = s;
setChanged();
notifyObservers();
}
}
and SecondWindow:
public class SecondWindow extends JFrame implements Observer{
private JPanel contentPane;
private Controller c;
private JTextPane txt = new JTextPane();
public SecondWindow () {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SecondWindow frame = new SecondWindow();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SecondWindow() {
initComponents();
createEvents();
c = Controller.getInstance();
}
public void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(1000, 0, 300,500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
txt.setBounds(0, 0, 280, 460);
txt.enable(false);
contentPane.add(txt);
}
public void update(Observable arg0 , Object arg1){
// Things to change here
}
I can't manage to put the variable c in the textField (like a txt.setText(c.getText) instruction). I'm sure that it reads the method update, but I don't know how to make sure it works.
Hint: Per the Observerable API the notifyObservers method has an overload that accepts any object as a parameter:
public void notifyObservers(Object arg)
This can even be a String. And as per the Observer API, this object is then passed into the update method in the observer, and you can use it there.
void update(Observable o,
Object arg)
arg - an argument passed to the notifyObservers method.
Separate side issue here:
contentPane.setLayout(null);
For most Swing aficionados, seeing this is like hearing nails on a chalkboard -- it's painful. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one. Instead you will want to study and learn the layout managers and then nest JPanels, each using its own layout manager to create pleasing and complex GUI's that look good on all OS's.
Side issue number two: your code is not Swing thread safe, since the Swing GUI could very well be notified by the observable off of the Swing event dispatch thread or EDT. While it is not likely to cause frequent or serious problems with this simple program, in general it would be better to use a SwingPropertyChangeSupport and PropertyChangeListeners rather than Observer / Observable if you can.
Next Side Issue
This:
public class Controller extends Observable(){
isn't compilable / kosher Java. Same for the duplicate parameter-less constructors for the SecondWindow class. Yes, we know what you're trying to do, but it's hard enough trying to understand someone else's code, you really don't want to make it harder by posting kind-of sort-of uncompilable code, trust me.
For example, something simple could be implemented in Swing using PropertyChangeListeners, like so:
import java.util.concurrent.TimeUnit;
public class Play2 {
public static void main(String[] args) {
Model2 model2 = new Model2();
View2 view2 = new View2();
new Controller2(model2, view2);
view2.show();
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// one of the few times it's OK to ignore an exception
}
String text = String.format("Counter Value: %d", i);
model2.setText(text);
}
}
}
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class Model2 {
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
public static final String TEXT = "text"; // name of our "bound" property
private String text = "";
public String getText() {
return text;
}
public void setText(String text) {
String oldValue = this.text;
String newValue = text;
this.text = text;
pcSupport.firePropertyChange(TEXT, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(name, listener);
}
public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(name, listener);
}
}
import javax.swing.*;
public class View2 {
private JPanel mainPanel = new JPanel();
private JTextField textField = new JTextField(10);
public View2() {
textField.setFocusable(false);
mainPanel.add(new JLabel("Text:"));
mainPanel.add(textField);
}
public JPanel getMainPanel() {
return mainPanel;
}
public void setText(String text) {
textField.setText(text);
}
public void show() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("View");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Controller2 {
private Model2 model2;
private View2 view2;
public Controller2(Model2 model2, View2 view2) {
this.model2 = model2;
this.view2 = view2;
model2.addPropertyChangeListener(Model2.TEXT, new ModelListener());
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
view2.setText((String) pcEvt.getNewValue());
}
}
}

Is it possible to have a JLabel changing its Text depending on a variable value?

I'created a JLabel that should display "TextA" if the variable count == -1,
"Text B" if the variable count == 0 and "TextC" if the variable count == 1.
I've used Swing to create my interface, which you can see below
TempConverter
The red rectangle shows where the JLabel should be.
I have tried creating 3 JLabels and changing the setVisible(Boolean) whenever the variable count value condition applies. This didn't work because I got the following error:
Exception in thread "main" java.lang.NullPointerException
at tempconverterUI.TempConverter.main(TempConverter.java:354)
C:\Users\x\AppData\Local\NetBeans\Cache\8.1\executor-snippets\run.xml:53: Java returned: 1
And the JLabels could not be placed in the same location in the GUI (overlapping was not possible).
I've tried using jLabel.setText() to change the Text displayed in the JLabel, whenever the variable condition applied. I got a similar error to the one above (if not the same).
I've read some other posts and researched further and found that some people suggested ActionListeners to be set but I am unsure that these will work with a simple variable, as opposed to a component in the GUI.
My Code is as follows:
package tempconverterUI;
import javax.swing.JOptionPane;
import messageBoxes.UserData;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.WString;
public class TempConverter extends javax.swing.JFrame {
public interface someLib extends Library
{
public int engStart();
public int endStop();
public int engCount();
public WString engGetLastError();
public int engSetAttribute(WString aszAttributeID, WString aszValue);
}
/**
* Creates new form TempConverter
*/
public TempConverter() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
The layout is created here, followed by the Temperature convertion methods and unrelated component's functionality (which I believe is not relevant in this case)
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
/**This is where the Login form gets created*/
UserData.popUp();
/**After this the Library functions are called, which will return the variable count value*/
someLib lib = (someLib) Native.loadLibrary("someLib", someLib.class);
int startResult = lib.engStart();
System.out.println(startResult);
if (startResult < 0)
{
System.out.println(lib.engGetLastError());
}
System.out.println(UserData.getAcInput());
int setAtResult = lib.engSetAttribute(new WString("CODE"), UserData.getAcInput());
System.out.println(setAtResult);
if (setAtResult < 0)
{
System.out.println(lib.engGetLastError());
}
And next is the piece of code from where I should control the JLabel Text to display
int count = lib.engCount();
System.out.println(count);
if (count == -1)
{
System.out.println(lib.engGetLastError());
}
else if (count == 0)
{
}
else
{
}
new TempConverter().setVisible(true);
}
// Variables declaration - do not modify
private javax.swing.JPanel bottomPanel;
private javax.swing.JButton convertButton;
private static javax.swing.JButton button;
private javax.swing.JTextField from;
private javax.swing.JComboBox<String> fromCombo;
private javax.swing.JLabel fromLabel;
private javax.swing.JLabel title;
private javax.swing.JTextField to;
private javax.swing.JComboBox<String> toCombo;
private javax.swing.JLabel toLabel;
private javax.swing.JPanel topPanel;
// End of variables declaration
}
Any help with this would be much appreciated. If you could include a simple code example as well, this would be fantastic as I am new to Java (and programming, in general).
Issues:
Don't set the JLabel visible, but rather add it initially to the GUI, leave it visible by default, and simply set its text via setText(...).
Give the class that holds the JLabel public methods that allow outside classes the ability to set the label's text. Something like public void setLabelText(String text), and in the method call setText(text) on the JLabel.
Debug your NullPointerException as you would any other NPE -- look at the stacktrace, find the line that throws it, and then look back into the code to see why a key variable on that line is null.
When and how you change the JLabel will depend on what event you want to listen for. If it is user input, then you will want to respond to that input, be it an ActionListener added to a JButton or to a JTextField, or an itemListener added to a JRadioButton.
If you want instead to listen for the change in state of a variable, no matter how the variable is changed, then make it a "bound property" (tutorial) using PropertyChangeSupport and a PropertyChangeListener.
For an example of the latter:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
#SuppressWarnings("serial")
public class ShowCount extends JPanel {
private static final int TIMER_DELAY = 1000;
private JLabel countLabel = new JLabel(" ");
private CountModel model = new CountModel();
public ShowCount() {
model.addPropertyChangeListener(CountModel.COUNT, new ModelListener(this));
setPreferredSize(new Dimension(250, 50));
add(new JLabel("Count:"));
add(countLabel);
Timer timer = new Timer(TIMER_DELAY, new TimerListener(model));
timer.start();
}
public void setCountLabelText(String text) {
countLabel.setText(text);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
ShowCount mainPanel = new ShowCount();
JFrame frame = new JFrame("ShowCount");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
class CountModel {
public static final String COUNT = "count"; // for count "property"
// support object that will notify listeners of change
private SwingPropertyChangeSupport support = new SwingPropertyChangeSupport(this);
private int count = 0;
public int getCount() {
return count;
}
public void setCount(int count) {
int oldValue = this.count;
int newValue = count;
this.count = count;
// notify listeners that count has changed
support.firePropertyChange(COUNT, oldValue, newValue);
}
// two methods to allow listeners to register with support object
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
support.addPropertyChangeListener(propertyName, listener);
}
}
class ModelListener implements PropertyChangeListener {
private ShowCount showCount;
public ModelListener(ShowCount showCount) {
super();
this.showCount = showCount;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
int newValue = (int) evt.getNewValue();
showCount.setCountLabelText(String.format("%03d", newValue));
}
}
class TimerListener implements ActionListener {
private CountModel model;
public TimerListener(CountModel model) {
super();
this.model = model;
}
#Override
public void actionPerformed(ActionEvent e) {
int oldCount = model.getCount();
int newCount = oldCount + 1;
model.setCount(newCount);
}
}

Java JTextField information access from another class

I am using a gui with JTextFields to collect some information and then a JButton that takes that infomration and writes it to a file, sets the gui visibility to false, and then uses Runnable to create an instance of another JFrame from a different class to display a slideshow.
I would like to access some of the information for the JTextFields from the new JFrame slideshow. I have tried creating an object of the previous class with accessor methods, but the values keep coming back null (I know that I have done this correctly).
I'm worried that when the accessor methods go to check what the variables equal the JTextFields appear null to the new JFrame.
Below is the sscce that shows this problem.
package accessmain;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class AccessMain extends JFrame implements ActionListener
{
private static final int FRAMEWIDTH = 800;
private static final int FRAMEHEIGHT = 300;
private JPanel mainPanel;
private PrintWriter outputStream = null;
private JTextField subjectNumberText;
private String subjectNumberString;
public static void main(String[] args)
{
AccessMain gui = new AccessMain();
gui.setVisible(true);
}
public AccessMain()
{
super("Self Paced Slideshow");
setSize(FRAMEWIDTH, FRAMEHEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
//Begin Main Content Panel
mainPanel = new JPanel();
mainPanel.setBorder(new EmptyBorder(0,10,0,10));
mainPanel.setLayout(new GridLayout(7, 2));
mainPanel.setBackground(Color.WHITE);
add(mainPanel, BorderLayout.CENTER);
mainPanel.add(new JLabel("Subject Number: "));
subjectNumberText = new JTextField(30);
mainPanel.add(subjectNumberText);
mainPanel.add(new JLabel(""));
JButton launch = new JButton("Begin Slideshow");
launch.addActionListener(this);
mainPanel.add(launch);
//End Main Content Panel
}
#Override
public void actionPerformed(ActionEvent e)
{
String actionCommand = e.getActionCommand();
if(actionCommand.equals("Begin Slideshow"))
{
subjectNumberString = subjectNumberText.getText();
if(!(subjectNumberString.equals("")))
{
System.out.println(getSubjectNumber());
this.setVisible(false);
writeFile();
outputStream.println("Subject Number:\t" + subjectNumberString);
outputStream.close();
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
AccessClass testClass = new AccessClass();
testClass.setVisible(true);
}
});
}
else
{
//Add warning dialogue here later
}
}
}
private void writeFile()
{
try
{
outputStream = new PrintWriter(new FileOutputStream(subjectNumberString + ".txt", false));
}
catch(FileNotFoundException e)
{
System.out.println("Cannot find file " + subjectNumberString + ".txt or it could not be opened.");
System.exit(0);
}
}
public String getSubjectNumber()
{
return subjectNumberString;
}
}
And then creating a barebones class to show the loss of data:
package accessmain;
import javax.swing.*;
import java.awt.*;
public class AccessClass extends JFrame
{
AccessMain experiment = new AccessMain();
String subjectNumber = experiment.getSubjectNumber();
public AccessClass()
{
System.out.println(subjectNumber);
}
}
Hardcoding the accessor method with "test" like this:
public String getSubjectNumber()
{
return "test";
}
Running this method as below in the new JFrame:
SelfPaceMain experiment = new SelfPaceMain();
private String subjectNumber = experiment.getSubjectNumber();
System.out.println(subjectNumber);
Does cause the system to print "test". So the accessor methods seem to be working. However, trying to access the values from the JTextFields doesn't seem to work.
I would read the information from the file I create, but without being able to pass the subjectNumber (which is used as the name of the file), I can't tell the new class what file to open.
Is there a good way to pass data from JTextFields to other classes?
pass the argument 'AccessMain' or 'JTextField' to the second class:
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
AccessClass testClass = new AccessClass(AccessMain.this); //fixed this
testClass.setVisible(true);
}
});
Then reading the value of 'subjectNumber'(JTextField value) from the 'AccessMain' or 'JTextField' in the second class:
public class AccessClass extends JFrame
{
final AccessMain experiment;
public AccessClass(AccessMain experiment)
{
this.experiment = experiment;
}
public String getSubjectNumber(){
return experiment.getSubjectNumber();
}
}
Also, you should try Observer pattern.
A simple demo of Observalbe and Observer
Observable and Observer Objects

Access static variable from another class

I have two classes in same package. i have declared a static variable in one class and want to access that variable in another class.
Here is my code in which i have declared the static variable
public class wampusGUI extends javax.swing.JFrame {
static String userCommand;
public wampusGUI() {
initComponents();
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
userCommand = commandText.getText();
}
public static void main(String args[]) {
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
wampusGUI w = new wampusGUI();
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
Here is the code in which i want to access variable
public class Game {
private wampusGUI gui;
public Game(wampusGUI w) {
world = new World();
world.start();
gui = w;
}
public void play() {
gui.setTextArea(welcome());
gui.setTextArea(describe());
for (;;) {
String s = userCommand; // here value should come should
System.out.println(userCommand);
Command c = Command.create(s);
String r = c.perform(world);
// is game over?
if (r == null) {
break;
}
System.out.println(r);
}
System.out.println("Game over");
}
}
However, i can pass the variable from first class as a argument. but the problem is that, when i will run program the value is going null first time, which i dont want. i want when i enter value in textfield then it should go to another class.
Thank you.
Looking at your code, it seems you want to show dialogs to your user with a certain text
gui.setTextArea(welcome());
gui.setTextArea(describe());
and sometimes, that dialog should capture user input which is handled afterwards.
Those setTextArea calls are not what you want to use. The user will never see the welcome message as it will immediately be replaced by the describe message.
Make sure you do not block the Event Dispatch Thread (EDT) or nothing will be shown at all. I do not know what your Command class will do, but I see an infinite loop on the Event Dispatch Thread which is never a good thing. Take a look at the Concurrency in Swing tutorial for more information
Thanks to that for loop, the user will simply not be capable to input any command as the EDT is busy handling your loop. What you need is a blocking call allowing the user to provide input (not blocking the EDT, but just blocking the execution of your code). The static methods in the JOptionPane class are perfectly suited for this (e.g. the JOptionPane#showInputDialog). These methods also have a mechanism to pass the user input back to the calling code without any static variables, which solves your problem.
I suggest that you use a listener of one sort or another to allow the Game object to listen for and respond to changes in the state of the GUI object. There are several ways to do this, but one of the most elegant and useful I've found is to use Swing's own innate PropertyChangeSupport to allow you to use PropertyChangeListeners. All Swing components will allow you to add a PropertyChangeListener to it. And so I suggest that you do this, that you have Game add one to your WampusGUI class (which should be capitalized) object like so:
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
// ....
}
This will allow Game to listen for changes in the gui's state.
You'll then want to make the gui's userCommand String a "bound property" which means giving it a setter method that will fire the property change support notifying all listeners of change. I would do this like so:
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
// ....
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
Then you would only change this String's value via this setter method:
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
The Game's property change listener would then respond like so:
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
One of the beauties of this technique is that the observed class, the GUI, doesn't have to have any knowledge about the observer class (the Game). A small runnable example of this is like so:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
public class WampusGUI extends JFrame {
public static final String USER_COMMAND = "user command";
private String userCommand;
private JTextArea displayTextArea = new JTextArea(10, 30);
private JTextField commandText = new JTextField(10);
public WampusGUI() {
initComponents();
}
private void setUserCommand(String userCommand) {
String oldValue = this.userCommand;
String newValue = userCommand;
this.userCommand = userCommand;
firePropertyChange(USER_COMMAND, oldValue, newValue);
}
private void initComponents() {
displayTextArea.setEditable(false);
displayTextArea.setFocusable(false);
JButton enterButton = new JButton("Enter Command");
enterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
enterButtonActionPerformed(evt);
}
});
JPanel commandPanel = new JPanel();
commandPanel.add(commandText);
commandPanel.add(Box.createHorizontalStrut(15));
commandPanel.add(enterButton);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BorderLayout());
mainPanel.add(new JScrollPane(displayTextArea));
mainPanel.add(commandPanel, BorderLayout.SOUTH);
add(mainPanel);
}
public void setTextArea(String text) {
displayTextArea.append(text);
}
private void enterButtonActionPerformed(java.awt.event.ActionEvent evt) {
setUserCommand(commandText.getText());
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
WampusGUI w = new WampusGUI();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
w.pack();
w.setLocationRelativeTo(null);
w.setVisible(true);
Game g = new Game(w);
g.play();
}
});
}
}
class Game {
private WampusGUI gui;
public Game(WampusGUI w) {
gui = w;
gui.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
// is the property being changed the one we're interested in?
if (WampusGUI.USER_COMMAND.equals(pcEvt.getPropertyName())) {
// get user command:
String userCommand = pcEvt.getNewValue().toString();
// then we can do with it what we want
play(userCommand);
}
}
});
}
public void play() {
gui.setTextArea("Welcome!\n");
gui.setTextArea("Please enjoy the game!\n");
}
public void play(String userCommand) {
// here we can do what we want with the String. For instance we can display it in the gui:
gui.setTextArea("User entered: " + userCommand + "\n");
}
}
I agree with Jon Skeet that this is not a good solution...
But in case u want an dirty solution to ur problem then u can try this:
public class wampusGUI extends javax.swing.JFrame
{
private static wampusGUI myInstance;
public wampusGUI( )
{
myInstance = this;
initComponents();
}
public static void getUserCommand()
{
if(myInstance!=null)
{
return myInstance.commandText.getText();
}
else
{
return null;
}
}
......
......
}
in the other class use:
public void play()
{
.....
//String s = userCommand; // here value should come should
String s = wampusGUI.getUserCommand();
.....
}
This kind of code is there in some of our legacy projects... and I hate this.

Categories