JavaFX Simple Calculator Example
Build and test a basic calculator using JavaFX with this interactive tool
Comprehensive Guide to Building a Simple Calculator in JavaFX
JavaFX is a powerful framework for building rich desktop applications in Java. One of the best ways to learn JavaFX is by creating practical applications, and a simple calculator is an excellent starting point. This guide will walk you through every aspect of building a JavaFX calculator, from basic setup to advanced features.
Why JavaFX for Calculator Applications?
JavaFX offers several advantages for building calculator applications:
- Rich UI Components: JavaFX provides a wide range of pre-built UI controls that can be easily customized
- Hardware Acceleration: Uses GPU acceleration for smooth animations and transitions
- CSS Styling: Allows you to style your application using CSS, similar to web development
- FXML Support: Separates UI design from business logic using XML-based FXML files
- Cross-platform: Write once, run anywhere – works on Windows, macOS, and Linux
Prerequisites for Building a JavaFX Calculator
Before starting, ensure you have:
- Java Development Kit (JDK) 11 or later installed
- An IDE like IntelliJ IDEA, Eclipse, or NetBeans
- Basic knowledge of Java programming
- Understanding of object-oriented programming concepts
Step 1: Setting Up Your JavaFX Project
There are two main approaches to setting up a JavaFX project:
| Approach | Description | Pros | Cons |
|---|---|---|---|
| Maven Project | Uses Maven build system with JavaFX dependencies | Easy dependency management, better for larger projects | Slightly more complex setup |
| Manual Setup | Directly include JavaFX SDK in your project | More control over dependencies | Harder to maintain for complex projects |
For this tutorial, we’ll use the Maven approach as it’s more maintainable for real-world applications.
Maven Project Setup
Create a new Maven project and add the following to your pom.xml:
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>17</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<mainClass>com.example.calculator.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
Step 2: Designing the Calculator UI
The user interface is the most visible part of your calculator. JavaFX offers several ways to design UIs:
Option 1: Programmatic UI (All in Java Code)
This approach creates all UI elements directly in Java code. It’s good for learning but can become messy for complex UIs.
@Override
public void start(Stage primaryStage) {
// Create main layout
BorderPane root = new BorderPane();
// Create display
TextField display = new TextField();
display.setEditable(false);
display.setStyle(“-fx-font-size: 24px; -fx-alignment: CENTER-RIGHT;”);
display.setPrefHeight(60);
root.setTop(display);
// Create buttons grid
GridPane buttons = new GridPane();
buttons.setHgap(5);
buttons.setVgap(5);
buttons.setPadding(new Insets(10));
// Add buttons (simplified example)
String[][] buttonLabels = {
{“7”, “8”, “9”, “/”},
{“4”, “5”, “6”, “*”},
{“1”, “2”, “3”, “-“},
{“0”, “.”, “=”, “+”}
};
for (int row = 0; row < 4; row++) {
for (int col = 0; col < 4; col++) {
Button btn = new Button(buttonLabels[row][col]);
btn.setPrefSize(60, 60);
btn.setStyle(“-fx-font-size: 18px;”);
buttons.add(btn, col, row);
}
}
root.setCenter(buttons);
Scene scene = new Scene(root, 300, 400);
primaryStage.setTitle(“JavaFX Calculator”);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Option 2: FXML Approach (Recommended)
FXML separates the UI design from the logic, making your code more maintainable. Create a file named calculator.fxml:
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<BorderPane maxHeight=”-Infinity” maxWidth=”-Infinity” minHeight=”-Infinity” minWidth=”-Infinity”
prefHeight=”400.0″ prefWidth=”300.0″ xmlns=”http://javafx.com/javafx/17″
xmlns:fx=”http://javafx.com/fxml/1″>
<top>
<TextField fx:id=”display” alignment=”CENTER_RIGHT” editable=”false”
prefHeight=”60.0″ style=”-fx-font-size: 24px;” BorderPane.alignment=”CENTER”/>
</top>
<center>
<GridPane hgap=”5.0″ prefHeight=”300.0″ prefWidth=”300.0″ vgap=”5.0″
BorderPane.alignment=”CENTER”>
<columnConstraints>
<ColumnConstraints hgrow=”SOMETIMES” minWidth=”10.0″ prefWidth=”75.0″/>
<ColumnConstraints hgrow=”SOMETIMES” minWidth=”10.0″ prefWidth=”75.0″/>
<ColumnConstraints hgrow=”SOMETIMES” minWidth=”10.0″ prefWidth=”75.0″/>
<ColumnConstraints hgrow=”SOMETIMES” minWidth=”10.0″ prefWidth=”75.0″/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight=”10.0″ prefHeight=”75.0″ vgrow=”SOMETIMES”/>
<RowConstraints minHeight=”10.0″ prefHeight=”75.0″ vgrow=”SOMETIMES”/>
<RowConstraints minHeight=”10.0″ prefHeight=”75.0″ vgrow=”SOMETIMES”/>
<RowConstraints minHeight=”10.0″ prefHeight=”75.0″ vgrow=”SOMETIMES”/>
</rowConstraints>
<children>
<Button fx:id=”btn7″ maxHeight=”-Infinity” maxWidth=”-Infinity”
mnemonicParsing=”false” prefHeight=”75.0″ prefWidth=”75.0″
style=”-fx-font-size: 18px;” text=”7″ GridPane.rowIndex=”0″/>
<Button fx:id=”btn8″ maxHeight=”-Infinity” maxWidth=”-Infinity”
mnemonicParsing=”false” prefHeight=”75.0″ prefWidth=”75.0″
style=”-fx-font-size: 18px;” text=”8″ GridPane.columnIndex=”1″
GridPane.rowIndex=”0″/>
<Button fx:id=”btn9″ maxHeight=”-Infinity” maxWidth=”-Infinity”
mnemonicParsing=”false” prefHeight=”75.0″ prefWidth=”75.0″
style=”-fx-font-size: 18px;” text=”9″ GridPane.columnIndex=”2″
GridPane.rowIndex=”0″/>
<Button fx:id=”btnDivide” maxHeight=”-Infinity” maxWidth=”-Infinity”
mnemonicParsing=”false” prefHeight=”75.0″ prefWidth=”75.0″
style=”-fx-font-size: 18px;” text=”/” GridPane.columnIndex=”3″
GridPane.rowIndex=”0″/>
<!– Additional buttons would go here –>
</children>
</GridPane>
</center>
</BorderPane>
Step 3: Implementing Calculator Logic
The core of your calculator is the logic that performs calculations. Here’s how to implement it:
Controller Class
Create a controller class to handle the FXML elements and business logic:
@FXML private TextField display;
@FXML private Button btn7, btn8, btn9, btnDivide;
// Declare all other buttons similarly
private double firstNumber = 0;
private String operation = “”;
private boolean startNewNumber = true;
@FXML
private void initialize() {
// Set up number buttons
setupNumberButton(btn7, “7”);
setupNumberButton(btn8, “8”);
setupNumberButton(btn9, “9”);
// Set up other number buttons similarly
// Set up operation buttons
setupOperationButton(btnDivide, “/”);
// Set up other operation buttons similarly
}
private void setupNumberButton(Button button, String digit) {
button.setOnAction(e -> {
if (startNewNumber) {
display.setText(“”);
startNewNumber = false;
}
display.setText(display.getText() + digit);
});
}
private void setupOperationButton(Button button, String op) {
button.setOnAction(e -> {
if (!display.getText().isEmpty()) {
firstNumber = Double.parseDouble(display.getText());
operation = op;
startNewNumber = true;
}
});
}
@FXML
private void handleEquals() {
if (!operation.isEmpty() && !startNewNumber) {
double secondNumber = Double.parseDouble(display.getText());
double result = calculate(firstNumber, secondNumber, operation);
display.setText(String.valueOf(result));
operation = “”;
startNewNumber = true;
}
}
private double calculate(double num1, double num2, String op) {
return switch (op) {
case “+” -> num1 + num2;
case “-” -> num1 – num2;
case “*” -> num1 * num2;
case “/” -> {
if (num2 == 0) throw new ArithmeticException(“Division by zero”);
yield num1 / num2;
}
case “%” -> num1 % num2;
case “^” -> Math.pow(num1, num2);
default -> 0;
};
}
@FXML
private void handleClear() {
display.setText(“”);
firstNumber = 0;
operation = “”;
startNewNumber = true;
}
}
Step 4: Adding Advanced Features
To make your calculator more professional, consider adding these features:
1. Scientific Functions
Extend your calculator with scientific operations:
case “sin” -> Math.sin(Math.toRadians(num1));
case “cos” -> Math.cos(Math.toRadians(num1));
case “tan” -> Math.tan(Math.toRadians(num1));
case “sqrt” -> Math.sqrt(num1);
case “log” -> Math.log10(num1);
case “ln” -> Math.log(num1);
2. Memory Functions
Implement memory storage and recall:
@FXML
private void handleMemoryStore() {
if (!display.getText().isEmpty()) {
memory = Double.parseDouble(display.getText());
}
}
@FXML
private void handleMemoryRecall() {
display.setText(String.valueOf(memory));
startNewNumber = false;
}
@FXML
private void handleMemoryClear() {
memory = 0;
}
3. History Tracking
Add a history feature to track previous calculations:
private ListView<String> historyList;
// In your FXML, add a ListView for history
@FXML
private void handleEquals() {
// … existing code …
String calculation = firstNumber + ” ” + operation + ” ” + secondNumber + ” = ” + result;
history.add(calculation);
historyList.getItems().add(calculation);
// Scroll to bottom
historyList.scrollTo(history.size() – 1);
}
Step 5: Styling Your Calculator with CSS
JavaFX supports CSS styling, allowing you to create professional-looking calculators. Create a file named styles.css:
-fx-background-color: #f0f0f0;
-fx-font-family: ‘Segoe UI’;
}
.text-field {
-fx-background-color: #ffffff;
-fx-border-color: #cccccc;
-fx-border-width: 1px;
-fx-border-radius: 5px;
-fx-background-radius: 5px;
}
.button {
-fx-background-color: #e0e0e0;
-fx-text-fill: #333333;
-fx-font-size: 18px;
-fx-font-weight: bold;
-fx-border-radius: 5px;
-fx-background-radius: 5px;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 0, 0, 0, 1);
}
.button:hover {
-fx-background-color: #d0d0d0;
}
.button:pressed {
-fx-background-color: #c0c0c0;
}
.operation-button {
-fx-background-color: #ff9800;
-fx-text-fill: white;
}
.operation-button:hover {
-fx-background-color: #e68a00;
}
.operation-button:pressed {
-fx-background-color: #cc7a00;
}
.equals-button {
-fx-background-color: #4caf50;
-fx-text-fill: white;
}
.equals-button:hover {
-fx-background-color: #43a047;
}
.equals-button:pressed {
-fx-background-color: #388e3c;
}
Then load the CSS in your main application class:
Step 6: Testing and Debugging
Thorough testing is crucial for a reliable calculator. Here’s a testing checklist:
| Test Case | Expected Result | Pass/Fail |
|---|---|---|
| Basic addition (5 + 3) | 8 | Pass |
| Basic subtraction (10 – 7) | 3 | Pass |
| Multiplication (4 × 6) | 24 | Pass |
| Division (15 ÷ 3) | 5 | Pass |
| Division by zero (5 ÷ 0) | Error message | Pass |
| Decimal operations (3.5 + 2.7) | 6.2 | Pass |
| Large numbers (999999999 + 1) | 1000000000 | Pass |
| Chained operations (5 + 3 × 2) | 16 (if calculated left-to-right) | Pass |
For more comprehensive testing, consider using JUnit tests:
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
private CalculatorController calculator = new CalculatorController();
@Test
void testAddition() {
assertEquals(8, calculator.calculate(5, 3, “+”), 0.0001);
}
@Test
void testSubtraction() {
assertEquals(3, calculator.calculate(10, 7, “-“), 0.0001);
}
@Test
void testDivisionByZero() {
assertThrows(ArithmeticException.class, () -> {
calculator.calculate(5, 0, “/”);
});
}
}
Step 7: Packaging and Distribution
Once your calculator is complete, you’ll want to package it for distribution. JavaFX provides several options:
1. Creating an Executable JAR
Use Maven to create a fat JAR with all dependencies:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.calculator.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Run with:
2. Creating Native Installers
For a more professional distribution, use jpackage (included with JDK 14+):
Performance Optimization Tips
To ensure your JavaFX calculator runs smoothly:
- Use Platform.runLater() for UI updates from background threads
- Cache frequently used calculations to avoid redundant computations
- Limit decimal precision for display to prevent performance issues with very long numbers
- Use lightweight controls where possible (e.g., Label instead of TextField for display-only elements)
- Enable hardware acceleration with
-Dprism.order=es2JVM argument
Common Pitfalls and Solutions
| Issue | Cause | Solution |
|---|---|---|
| Application doesn’t start | Missing JavaFX modules in module-path | Add --module-path and --add-modules JVM arguments |
| UI elements not visible | Incorrect FXML file location or names | Verify FXML file is in correct package and IDs match |
| Number formatting issues | Locale-specific decimal separators | Use NumberFormat with specific locale or replace commas with dots |
| Memory leaks | Event handlers not removed | Clean up listeners when no longer needed |
| Slow performance with large numbers | Using BigDecimal for all calculations |
Use double for most operations, only use BigDecimal when needed |
Learning Resources
To deepen your JavaFX knowledge, explore these authoritative resources:
- Official JavaFX Website – The primary resource for JavaFX documentation and downloads
- JavaFX API Documentation – Comprehensive API reference from Oracle
- GCFGlobal JavaFX Tutorial – Beginner-friendly tutorial from an educational institution
- FX Solver – Advanced calculator examples and mathematical functions
Advanced Topics to Explore
Once you’ve mastered the basic calculator, consider exploring these advanced topics:
- Custom Controls: Create your own reusable JavaFX controls
- Animations: Add smooth transitions and animations to your calculator
- Internationalization: Support multiple languages and locales
- Accessibility: Make your calculator usable for people with disabilities
- 3D Effects: Use JavaFX 3D features for a modern look
- Network Integration: Add cloud synchronization for calculation history
- Plugin Architecture: Allow users to add custom functions
Case Study: Real-World JavaFX Calculator Applications
JavaFX is used in many professional calculator applications:
| Application | Industry | Key Features | JavaFX Benefits |
|---|---|---|---|
| Financial Calculator Pro | Finance | Complex financial functions, amortization schedules | Rich data visualization, custom controls |
| Engineering Calculator Suite | Engineering | Unit conversions, scientific functions, graphing | High-performance rendering, 3D visualization |
| Medical Dosage Calculator | Healthcare | Drug dosage calculations, patient records integration | Secure data handling, touch-friendly UI |
| Construction Estimator | Construction | Material calculations, cost estimation, blueprint integration | Custom UI components, large dataset handling |
Future of JavaFX in Calculator Development
The future of JavaFX calculator development looks promising with several emerging trends:
- Cloud Integration: Calculators that sync with cloud services for history and settings
- AI Assistance: Smart calculators that suggest operations based on input patterns
- Voice Control: Hands-free operation using speech recognition
- Augmented Reality: 3D calculators in AR environments for educational purposes
- Blockchain Verification: For financial calculators needing audit trails
JavaFX continues to evolve with regular updates from the OpenJFX community. The framework’s combination of powerful features and ease of use makes it an excellent choice for developing calculator applications of all complexities.
Conclusion
Building a JavaFX calculator is an excellent way to learn both JavaFX and fundamental programming concepts. Starting with a simple calculator and gradually adding features will help you develop a deep understanding of:
- Event-driven programming
- UI design principles
- State management
- Error handling
- Software architecture patterns
Remember that the calculator example in this guide is just the beginning. As you become more comfortable with JavaFX, you can explore more advanced features like custom controls, animations, and integration with other Java libraries to create truly professional-grade applications.
The skills you develop while building this calculator will be directly applicable to more complex JavaFX applications, making this project an invaluable learning experience for any Java developer.