I have this calculator working, but I can\'t figure out how to get the value in the resultpane
to the first textbox when you click the \"finish\" button.
Give your ResultPane a new public method that allows you to extract the text from its JLabel, something like:
public String getResultText() {
return result.getText();
}
And then call it when you want to get the text of that field:
public class ToText1Action extends AbstractAction {
public ToText1Action() {
putValue(NAME, "Finish");
}
@Override
public void actionPerformed(ActionEvent e) {
String resultText = resultPane.getResultText();
double number = // extract number from resultText
firstNumberField.setText(number);
}
}
Better still, simply store the latest result value in a double variable that is an instance field of the class, say called lastResult
and set the firstNumberField with that.
Note that you should not have empty catch blocks.
Edit
For example:
public class CalculatorPane extends JPanel {
private final ResultPane resultPane;
private final JLabel firstNumberLabel = new JLabel("First Number:");
private final JLabel secondNumberLabel = new JLabel("Second Number:");
private final JTextField firstNumberField = new JTextField(5);
private final JTextField secondNumberField = new JTextField(5);
private double result = 0.0; // ****** declared here
// ***** etc..... *****
public class DivideAction extends AbstractAction {
public DivideAction() {
putValue(NAME, "/");
}
@Override
public void actionPerformed(ActionEvent e) {
try {
double num1 = Double.parseDouble(firstNumberField.getText());
double num2 = Double.parseDouble(secondNumberField.getText());
result = num1 / num2; // ****** note variable not declared
resultPane.setResult(result);
} catch (NumberFormatException exp) {
// show error message
// set text fields to "0"
}
}
}
public class ToText1Action extends AbstractAction {
public ToText1Action() {
super("Finish");
putValue(MNEMONIC_KEY, KeyEvent.VK_F);
}
@Override
public void actionPerformed(ActionEvent e) {
firstNumberField.setText(String.format("%.2f", result));
}
}
As another approach, you could establish some kind of model, which is shared between the actions and the components, this helps de-couple of the UI as it's main linking factor is the model, not other UI elements.
For the ToText1Action
, you will also need to pass it the field you want updated, but since it's primarily a JTextField
, it shouldn't be to bad...
So, the primary change is the inclusion of some kind of model, which provides means to set the result, get the result and provide event notification when the model changes, so those interested parties can update themselves as required, for example...
public interface ResultsModel {
public void setResult(double value);
public double getResult();
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
public class DefaultResultsModel implements ResultsModel {
private List<ChangeListener> changeListeners;
private double result;
public DefaultResultsModel() {
changeListeners = new ArrayList<>(25);
}
public double getResult() {
return result;
}
public void setResult(double value) {
if (value != result) {
this.result = value;
fireStateChanged();
}
}
protected void fireStateChanged() {
if (!changeListeners.isEmpty()) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : changeListeners) {
listener.stateChanged(evt);
}
}
}
@Override
public void addChangeListener(ChangeListener listener) {
changeListeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
changeListeners.remove(listener);
}
}
The ResultsPane
and each action will need to be changed so that they can work with the model instead of directly with each. This means that the ResultsPane
will now listen to changes in the model instead of been updated directly
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import static javax.swing.Action.NAME;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestCalculator {
public static void main(String[] args) {
new TestCalculator();
}
public TestCalculator() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
ResultsModel model = new DefaultResultsModel();
ResultPane resultPane = new ResultPane();
resultPane.setModel(model);
JFrame frame = new JFrame("Testing");
frame.setGlassPane(resultPane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new CalculatorPane(model));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface ResultsModel {
public void setResult(double value);
public double getResult();
public void addChangeListener(ChangeListener listener);
public void removeChangeListener(ChangeListener listener);
}
public class DefaultResultsModel implements ResultsModel {
private List<ChangeListener> changeListeners;
private double result;
public DefaultResultsModel() {
changeListeners = new ArrayList<>(25);
}
@Override
public double getResult() {
return result;
}
@Override
public void setResult(double value) {
if (value != result) {
this.result = value;
fireStateChanged();
}
}
protected void fireStateChanged() {
if (!changeListeners.isEmpty()) {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener listener : changeListeners) {
listener.stateChanged(evt);
}
}
}
@Override
public void addChangeListener(ChangeListener listener) {
changeListeners.add(listener);
}
@Override
public void removeChangeListener(ChangeListener listener) {
changeListeners.remove(listener);
}
}
public class ResultPane extends JPanel implements ChangeListener {
private ResultsModel model;
private JLabel result;
private Timer timer;
private int xDelta = (Math.random() > 0.5) ? 1 : -1;
private int yDelta = (Math.random() > 0.5) ? 1 : -1;
public ResultPane() {
setOpaque(false);
setLayout(null);
result = new JLabel();
Font font = result.getFont();
font = font.deriveFont(Font.BOLD, 26f);
result.setFont(font);
add(result);
timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Point point = result.getLocation();
point.x += xDelta;
point.y += yDelta;
if (point.x < 0) {
point.x = 0;
xDelta *= -1;
} else if (point.x + result.getWidth() > getWidth()) {
point.x = getWidth() - result.getWidth();
xDelta *= -1;
}
if (point.y < 0) {
point.y = 0;
yDelta *= -1;
} else if (point.y + result.getHeight() > getHeight()) {
point.y = getHeight() - result.getHeight();
yDelta *= -1;
}
result.setLocation(point);
repaint();
}
});
timer.start();
}
public void setModel(ResultsModel value) {
if (value != model) {
if (model != null) {
model.removeChangeListener(this);
}
model = value;
if (model != null) {
model.addChangeListener(this);
}
}
}
public ResultsModel getModel() {
return model;
}
@Override
public void stateChanged(ChangeEvent e) {
double number = getModel().getResult();
result.setText("Result: " + NumberFormat.getNumberInstance().format(number));
result.setSize(result.getPreferredSize());
setVisible(true);
}
}
public class CalculatorPane extends JPanel {
private final JLabel firstNumberLabel = new JLabel("First Number:");
private final JLabel secondNumberLabel = new JLabel("Second Number:");
private final JTextField firstNumberField = new JTextField(5);
private final JTextField secondNumberField = new JTextField(5);
public CalculatorPane(ResultsModel model) {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JPanel fields = new JPanel();
fields.add(firstNumberLabel);
fields.add(firstNumberField);
fields.add(secondNumberLabel);
fields.add(secondNumberField);
add(fields, gbc);
JPanel buttons = new JPanel();
buttons.add(new JButton(new AddAction(model)));
buttons.add(new JButton(new SubtractAction(model)));
buttons.add(new JButton(new MultiplyAction(model)));
buttons.add(new JButton(new DivideAction(model)));
buttons.add(new JButton(new ToText1Action(firstNumberField, model)));
add(buttons, gbc);
}
public abstract class AbstractResultsAction extends AbstractAction {
private ResultsModel model;
public AbstractResultsAction(ResultsModel model) {
this.model = model;
}
public ResultsModel getModel() {
return model;
}
}
public class AddAction extends AbstractResultsAction {
public AddAction(ResultsModel model) {
super(model);
putValue(NAME, "+");
}
@Override
public void actionPerformed(ActionEvent e) {
try {
double num1 = Double.parseDouble(firstNumberField.getText());
double num2 = Double.parseDouble(secondNumberField.getText());
double result = num1 + num2;
//num1.setResult(firstNumberField);
getModel().setResult(result);
} catch (NumberFormatException exp) {
}
}
}
public class SubtractAction extends AbstractResultsAction {
public SubtractAction(ResultsModel model) {
super(model);
putValue(NAME, "-");
}
@Override
public void actionPerformed(ActionEvent e) {
try {
double num1 = Double.parseDouble(firstNumberField.getText());
double num2 = Double.parseDouble(secondNumberField.getText());
double result = num1 - num2;
getModel().setResult(result);
} catch (NumberFormatException exp) {
}
}
}
public class MultiplyAction extends AbstractResultsAction {
public MultiplyAction(ResultsModel model) {
super(model);
putValue(NAME, "x");
}
@Override
public void actionPerformed(ActionEvent e) {
try {
double num1 = Double.parseDouble(firstNumberField.getText());
double num2 = Double.parseDouble(secondNumberField.getText());
double result = num1 * num2;
getModel().setResult(result);
} catch (NumberFormatException exp) {
}
}
}
public class DivideAction extends AbstractResultsAction {
public DivideAction(ResultsModel model) {
super(model);
putValue(NAME, "/");
}
@Override
public void actionPerformed(ActionEvent e) {
try {
double num1 = Double.parseDouble(firstNumberField.getText());
double num2 = Double.parseDouble(secondNumberField.getText());
double result = num1 / num2;
getModel().setResult(result);
} catch (NumberFormatException exp) {
}
}
}
public class ToText1Action extends AbstractResultsAction {
private JTextField field;
public ToText1Action(JTextField field, ResultsModel model) {
super(model);
putValue(NAME, "Finish");
this.field = field;
}
public JTextField getField() {
return field;
}
@Override
public void actionPerformed(ActionEvent e) {
try {
double result = getModel().getResult();
getField().setText(NumberFormat.getNumberInstance().format(result));
//i would like to put it here
} catch (NumberFormatException exp) {
}
}
}
}
}
This is an example of the observer pattern