-
-
Save jewelsea/4344564 to your computer and use it in GitHub Desktop.
import javafx.application.Application; | |
import javafx.beans.binding.Bindings; | |
import javafx.beans.property.*; | |
import javafx.geometry.Pos; | |
import javafx.scene.Scene; | |
import javafx.scene.control.*; | |
import javafx.scene.input.KeyEvent; | |
import javafx.scene.layout.*; | |
import javafx.stage.*; | |
import java.util.*; | |
// a simple JavaFX calculator. | |
public class Calc extends Application { | |
private static final String[][] template = { | |
{"7", "8", "9", "/"}, | |
{"4", "5", "6", "*"}, | |
{"1", "2", "3", "-"}, | |
{"0", "c", "=", "+"} | |
}; | |
private final Map<String, Button> accelerators = new HashMap<>(); | |
private final DoubleProperty stackValue = new SimpleDoubleProperty(); | |
private final DoubleProperty value = new SimpleDoubleProperty(); | |
private enum Op {NOOP, ADD, SUBTRACT, MULTIPLY, DIVIDE} | |
private Op curOp = Op.NOOP; | |
private Op stackOp = Op.NOOP; | |
public static void main(String[] args) { | |
launch(args); | |
} | |
@Override | |
public void start(Stage stage) { | |
final TextField screen = createScreen(); | |
final TilePane buttons = createButtons(); | |
stage.setTitle("Calc"); | |
stage.initStyle(StageStyle.UTILITY); | |
stage.setResizable(false); | |
stage.setScene(new Scene(createLayout(screen, buttons))); | |
stage.show(); | |
} | |
private VBox createLayout(TextField screen, TilePane buttons) { | |
final VBox layout = new VBox(20); | |
layout.setAlignment(Pos.CENTER); | |
layout.setStyle("-fx-background-color: chocolate; -fx-padding: 20; -fx-font-size: 20;"); | |
layout.getChildren().setAll(screen, buttons); | |
handleAccelerators(layout); | |
screen.prefWidthProperty().bind(buttons.widthProperty()); | |
return layout; | |
} | |
private void handleAccelerators(VBox layout) { | |
layout.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> { | |
Button activated = accelerators.get(keyEvent.getText()); | |
if (activated != null) { | |
activated.fire(); | |
} | |
}); | |
} | |
private TextField createScreen() { | |
final TextField screen = new TextField(); | |
screen.setStyle("-fx-background-color: aquamarine;"); | |
screen.setAlignment(Pos.CENTER_RIGHT); | |
screen.setEditable(false); | |
screen.textProperty().bind(Bindings.format("%.0f", value)); | |
return screen; | |
} | |
private TilePane createButtons() { | |
TilePane buttons = new TilePane(); | |
buttons.setVgap(7); | |
buttons.setHgap(7); | |
buttons.setPrefColumns(template[0].length); | |
for (String[] r : template) { | |
for (String s : r) { | |
buttons.getChildren().add(createButton(s)); | |
} | |
} | |
return buttons; | |
} | |
private Button createButton(final String s) { | |
Button button = makeStandardButton(s); | |
if (s.matches("[0-9]")) { | |
makeNumericButton(s, button); | |
} else { | |
final ObjectProperty<Op> triggerOp = determineOperand(s); | |
if (triggerOp.get() != Op.NOOP) { | |
makeOperandButton(button, triggerOp); | |
} else if ("c".equals(s)) { | |
makeClearButton(button); | |
} else if ("=".equals(s)) { | |
makeEqualsButton(button); | |
} | |
} | |
return button; | |
} | |
private ObjectProperty<Op> determineOperand(String s) { | |
final ObjectProperty<Op> triggerOp = new SimpleObjectProperty<>(Op.NOOP); | |
switch (s) { | |
case "+" -> triggerOp.set(Op.ADD); | |
case "-" -> triggerOp.set(Op.SUBTRACT); | |
case "*" -> triggerOp.set(Op.MULTIPLY); | |
case "/" -> triggerOp.set(Op.DIVIDE); | |
} | |
return triggerOp; | |
} | |
private void makeOperandButton(Button button, final ObjectProperty<Op> triggerOp) { | |
button.setStyle("-fx-base: lightgray;"); | |
button.setOnAction(actionEvent -> curOp = triggerOp.get()); | |
} | |
private Button makeStandardButton(String s) { | |
Button button = new Button(s); | |
button.setStyle("-fx-base: beige;"); | |
accelerators.put(s, button); | |
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); | |
return button; | |
} | |
private void makeNumericButton(final String s, Button button) { | |
button.setOnAction(actionEvent -> { | |
if (curOp == Op.NOOP) { | |
value.set(value.get() * 10 + Integer.parseInt(s)); | |
} else { | |
stackValue.set(value.get()); | |
value.set(Integer.parseInt(s)); | |
stackOp = curOp; | |
curOp = Op.NOOP; | |
} | |
}); | |
} | |
private void makeClearButton(Button button) { | |
button.setStyle("-fx-base: mistyrose;"); | |
button.setOnAction(actionEvent -> value.set(0)); | |
} | |
private void makeEqualsButton(Button button) { | |
button.setStyle("-fx-base: ghostwhite;"); | |
button.setOnAction(actionEvent -> { | |
switch (stackOp) { | |
case ADD -> value.set(stackValue.get() + value.get()); | |
case SUBTRACT -> value.set(stackValue.get() - value.get()); | |
case MULTIPLY -> value.set(stackValue.get() * value.get()); | |
case DIVIDE -> value.set(stackValue.get() / value.get()); | |
} | |
}); | |
} | |
} |
Note, as the title says, this is a very simple calculator. It does not handle things like non-integral values and operator precedence, memory recall, bracketed expressions, etc. It's just basic add, subtract, multiply divide immediately applied on the latest displayed value.
This is coded as a single Java file - a more flexible design would be to have the layout in FXML, the styling in an external CSS stylesheet and the calculator logic less intertwined with the UI display.
It doesn't work for sth like "1+2*3"
Yes xiyun, I already noted that "It does not handle things like non-integral values and operator precedence". Feel free to fork the code and add such functionality if you wish.
thanks lot for this javafx introduction, Jewelsea
Good stuff!
For anybody who would like to improve the operation of this calculator, you could look at combining its operation with an implementation of the shunting-yard algorithm as a basis for providing the capability to handle operator precedence by converting the input to reverse polish notation and then evaluating it.
it is giving me 9 errors
thx bro
Can anybody tell us how can we make the sum of more than two numbers in above mentioned code?
Can anybody tell us how can we make the sum of more than two numbers in above mentioned code?
@Kapil-Joban did you read @jewelsea's comment dated Mar 9, 2018? The shunting-yard algorithm is proposed in that comment.
Can anybody tell us how can we make the sum of more than two numbers in above mentioned code?
@Kapil-Joban , You can also have a look at javax
ScriptEngineManager
. I guess it still works. I am not sure. https://github.com/sedj601/CalculatorFX2/blob/master/CalculatorController.java
Oracle JavaFX forum thread: Simple calculator control. Anyone ?