JavaFX - Modern GUI Development
Master JavaFX: Learn to create modern, rich desktop applications with Scene Builder, FXML, CSS styling, animations, charts, and 3D graphics using Java's premier GUI framework.
Rich UI Controls
50+ Built-in controls
FXML & CSS
Separation of concerns
Charts & 3D
Advanced visualization
Hardware Accelerated
GPU rendering
1. Introduction to JavaFX
JavaFX is a modern, rich-client platform for building cross-platform desktop applications. It replaces Swing as the standard GUI library for Java SE and provides hardware-accelerated graphics, CSS styling, FXML for declarative UI design, and a comprehensive set of UI controls.
Key Features
- Hardware Acceleration: GPU-accelerated rendering
- FXML: XML-based UI declaration
- CSS Styling: Full CSS3 support for styling
- Scene Builder: Visual layout tool
- Rich Controls: 50+ built-in UI controls
- WebView: Embedded web browser
- 3D Graphics: Built-in 3D support
JavaFX vs Swing
- Performance: JavaFX uses hardware acceleration
- Styling: CSS vs Look and Feel
- Architecture: Scene Graph vs AWT/Swing
- Modern Features: Built-in animations, 3D, media
- Tooling: Scene Builder vs NetBeans Matisse
- Future: Actively developed vs maintenance
JavaFX Architecture
Scene Graph → Prism (Rendering) → Glass (Window Toolkit) → Quantum (Threading)
JavaFX uses a retained mode rendering model with a scene graph data structure for efficient UI updates and hardware-accelerated graphics.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FirstJavaFXApp extends Application {
@Override
public void start(Stage primaryStage) {
// Create UI controls
Label titleLabel = new Label("Welcome to JavaFX!");
titleLabel.setStyle("-fx-font-size: 24px; -fx-font-weight: bold; -fx-text-fill: #3D8B37;");
Label messageLabel = new Label("This is your first JavaFX application.");
messageLabel.setStyle("-fx-font-size: 14px; -fx-text-fill: #666;");
Button clickButton = new Button("Click Me!");
clickButton.setStyle("-fx-background-color: #3D8B37; -fx-text-fill: white; -fx-font-size: 16px; " +
"-fx-padding: 10px 20px; -fx-border-radius: 5px;");
Button exitButton = new Button("Exit");
exitButton.setStyle("-fx-background-color: #FF6B35; -fx-text-fill: white; -fx-font-size: 16px; " +
"-fx-padding: 10px 20px; -fx-border-radius: 5px;");
// Add event handlers
clickButton.setOnAction(event -> {
messageLabel.setText("Button clicked! Current time: " + java.time.LocalTime.now());
clickButton.setStyle("-fx-background-color: #00A8E8; -fx-text-fill: white; -fx-font-size: 16px; " +
"-fx-padding: 10px 20px; -fx-border-radius: 5px;");
});
exitButton.setOnAction(event -> {
System.out.println("Application exiting...");
primaryStage.close();
});
// Create layout
VBox root = new VBox(20); // 20 pixels spacing between children
root.setStyle("-fx-padding: 40px; -fx-background-color: #f8f9fa; -fx-alignment: center;");
root.getChildren().addAll(titleLabel, messageLabel, clickButton, exitButton);
// Create scene
Scene scene = new Scene(root, 400, 300);
// Configure stage (window)
primaryStage.setTitle("First JavaFX Application");
primaryStage.setScene(scene);
primaryStage.show();
// Window close request handler
primaryStage.setOnCloseRequest(event -> {
System.out.println("Window is closing...");
// You could add confirmation dialog here
});
}
public static void main(String[] args) {
// Launch the JavaFX application
launch(args);
}
}
Welcome to JavaFX!
This is your first JavaFX application.
2. Scene Builder & FXML
FXML is an XML-based markup language for defining JavaFX user interfaces. Scene Builder is a visual layout tool that generates FXML, allowing designers to create interfaces without writing code.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<VBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.LoginController"
spacing="20"
alignment="CENTER"
style="-fx-background-color: linear-gradient(to bottom right, #f8f9fa, #e9ecef);"
prefWidth="400" prefHeight="500">
<padding>
<Insets top="40" right="40" bottom="40" left="40"/>
</padding>
<!-- Header -->
<VBox spacing="10" alignment="CENTER">
<Label text="Welcome Back"
style="-fx-font-size: 32px; -fx-font-weight: bold; -fx-text-fill: #3D8B37;"/>
<Label text="Sign in to your account"
style="-fx-font-size: 14px; -fx-text-fill: #666;"/>
</VBox>
<!-- Login Form -->
<VBox spacing="15" prefWidth="300">
<!-- Username Field -->
<VBox spacing="5">
<Label text="Username" style="-fx-font-weight: bold;"/>
<TextField fx:id="usernameField"
promptText="Enter your username"
style="-fx-padding: 10px; -fx-font-size: 14px;"/>
</VBox>
<!-- Password Field -->
<VBox spacing="5">
<Label text="Password" style="-fx-font-weight: bold;"/>
<PasswordField fx:id="passwordField"
promptText="Enter your password"
style="-fx-padding: 10px; -fx-font-size: 14px;"/>
</VBox>
<!-- Remember Me Checkbox -->
<HBox spacing="10" alignment="CENTER_LEFT">
<CheckBox fx:id="rememberCheckbox" text="Remember me"/>
<Region HBox.hgrow="ALWAYS"/>
<Hyperlink text="Forgot password?" onAction="#onForgotPassword"/>
</HBox>
<!-- Login Button -->
<Button text="Sign In"
fx:id="loginButton"
onAction="#onLogin"
defaultButton="true"
style="-fx-background-color: #3D8B37; -fx-text-fill: white;
-fx-font-size: 16px; -fx-font-weight: bold;
-fx-padding: 12px 0; -fx-background-radius: 5px;"
prefWidth="300"/>
<!-- Error Message -->
<Label fx:id="errorLabel"
style="-fx-text-fill: #dc3545; -fx-font-size: 12px;"
visible="false"/>
</VBox>
<!-- Divider -->
<Separator prefWidth="300"/>
<!-- Social Login -->
<VBox spacing="10" alignment="CENTER">
<Label text="Or sign in with" style="-fx-text-fill: #666;"/>
<HBox spacing="15" alignment="CENTER">
<Button text="Google"
onAction="#onGoogleLogin"
style="-fx-background-color: white; -fx-text-fill: #666;
-fx-border-color: #ddd; -fx-border-width: 1px;
-fx-padding: 10px 20px;"/>
<Button text="GitHub"
onAction="#onGithubLogin"
style="-fx-background-color: #333; -fx-text-fill: white;
-fx-padding: 10px 20px;"/>
</HBox>
</VBox>
<!-- Sign Up Link -->
<HBox spacing="5" alignment="CENTER">
<Label text="Don't have an account?" style="-fx-text-fill: #666;"/>
<Hyperlink text="Sign up" onAction="#onSignUp"/>
</HBox>
</VBox>
package com.example;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.event.ActionEvent;
import javafx.scene.control.Alert.AlertType;
public class LoginController {
@FXML
private TextField usernameField;
@FXML
private PasswordField passwordField;
@FXML
private CheckBox rememberCheckbox;
@FXML
private Button loginButton;
@FXML
private Label errorLabel;
// Initialize method - called after FXML is loaded
@FXML
private void initialize() {
// Set up initial state
loginButton.setDisable(true);
// Add listeners for input validation
usernameField.textProperty().addListener((observable, oldValue, newValue) -> {
validateInput();
});
passwordField.textProperty().addListener((observable, oldValue, newValue) -> {
validateInput();
});
// Enter key support
usernameField.setOnAction(this::onLogin);
passwordField.setOnAction(this::onLogin);
}
@FXML
private void onLogin(ActionEvent event) {
String username = usernameField.getText();
String password = passwordField.getText();
boolean rememberMe = rememberCheckbox.isSelected();
// Clear previous errors
errorLabel.setVisible(false);
// Validate credentials (in real app, check against database)
if (isValidCredentials(username, password)) {
// Successful login
showAlert(AlertType.INFORMATION, "Login Successful",
"Welcome, " + username + "!");
if (rememberMe) {
saveCredentials(username, password);
}
// Navigate to main application
navigateToMainApp();
} else {
// Failed login
errorLabel.setText("Invalid username or password");
errorLabel.setVisible(true);
// Visual feedback
usernameField.setStyle("-fx-border-color: #dc3545; -fx-border-width: 2px;");
passwordField.setStyle("-fx-border-color: #dc3545; -fx-border-width: 2px;");
}
}
@FXML
private void onForgotPassword(ActionEvent event) {
showAlert(AlertType.INFORMATION, "Password Reset",
"A password reset link has been sent to your email.");
}
@FXML
private void onGoogleLogin(ActionEvent event) {
showAlert(AlertType.INFORMATION, "Google Login",
"Redirecting to Google authentication...");
}
@FXML
private void onGithubLogin(ActionEvent event) {
showAlert(AlertType.INFORMATION, "GitHub Login",
"Redirecting to GitHub authentication...");
}
@FXML
private void onSignUp(ActionEvent event) {
showAlert(AlertType.INFORMATION, "Sign Up",
"Redirecting to registration page...");
}
private void validateInput() {
String username = usernameField.getText();
String password = passwordField.getText();
boolean isValid = !username.trim().isEmpty() &&
!password.trim().isEmpty() &&
username.length() >= 3 &&
password.length() >= 6;
loginButton.setDisable(!isValid);
// Clear error styles when typing
if (!username.trim().isEmpty()) {
usernameField.setStyle("");
}
if (!password.trim().isEmpty()) {
passwordField.setStyle("");
}
}
private boolean isValidCredentials(String username, String password) {
// Demo validation - replace with real authentication
return "admin".equals(username) && "password123".equals(password);
}
private void saveCredentials(String username, String password) {
// Save to preferences or secure storage
System.out.println("Credentials saved for: " + username);
}
private void navigateToMainApp() {
// Code to switch to main application scene
System.out.println("Navigating to main application...");
}
private void showAlert(AlertType type, String title, String message) {
Alert alert = new Alert(type);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
}
package com.example;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// Load FXML file
FXMLLoader loader = new FXMLLoader(getClass().getResource("LoginForm.fxml"));
Parent root = loader.load();
// Get controller (optional - for advanced scenarios)
LoginController controller = loader.getController();
// Create scene
Scene scene = new Scene(root);
// Apply CSS stylesheet (optional)
scene.getStylesheets().add(getClass().getResource("styles.css").toExternalForm());
// Configure stage
primaryStage.setTitle("JavaFX Login Application");
primaryStage.setScene(scene);
primaryStage.setResizable(false);
// Show the stage
primaryStage.show();
// Center window on screen
primaryStage.centerOnScreen();
}
public static void main(String[] args) {
launch(args);
}
}
FXML Benefits
- Separation of Concerns: UI layout vs business logic
- Tool Support: Scene Builder visual design
- Maintainability: Easier to modify UI structure
- Internationalization: Easy to localize text
- Reusability: Can include other FXML files
- Performance: Loaded once, cached for reuse
Scene Builder Tips
- Use fx:id for controller access
- Set prefWidth/Height for responsive layouts
- Use Controller Factory for dependency injection
- Organize with AnchorPane or GridPane
- Preview with different CSS stylesheets
- Export for different screen resolutions
3. JavaFX UI Controls
JavaFX provides a comprehensive set of built-in UI controls for creating modern desktop applications. All controls support CSS styling, event handling, and data binding.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.time.LocalDate;
public class ControlsDemo extends Application {
@Override
public void start(Stage primaryStage) {
// Create main layout with tabs
TabPane tabPane = new TabPane();
// Tab 1: Basic Controls
Tab basicTab = new Tab("Basic Controls", createBasicControlsPane());
basicTab.setClosable(false);
// Tab 2: Selection Controls
Tab selectionTab = new Tab("Selection Controls", createSelectionControlsPane());
selectionTab.setClosable(false);
// Tab 3: Data Entry
Tab dataTab = new Tab("Data Entry", createDataEntryPane());
dataTab.setClosable(false);
// Tab 4: Advanced Controls
Tab advancedTab = new Tab("Advanced", createAdvancedControlsPane());
advancedTab.setClosable(false);
tabPane.getTabs().addAll(basicTab, selectionTab, dataTab, advancedTab);
// Create scene
Scene scene = new Scene(tabPane, 800, 600);
// Apply CSS
scene.getStylesheets().add(getClass().getResource("controls.css").toExternalForm());
// Configure stage
primaryStage.setTitle("JavaFX Controls Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
private VBox createBasicControlsPane() {
VBox vbox = new VBox(15);
vbox.setPadding(new Insets(20));
vbox.setStyle("-fx-background-color: #f8f9fa;");
// Labels
Label title = new Label("Basic Controls Demo");
title.setStyle("-fx-font-size: 24px; -fx-font-weight: bold; -fx-text-fill: #3D8B37;");
// Buttons
HBox buttonBox = new HBox(10);
Button primaryBtn = new Button("Primary");
primaryBtn.getStyleClass().add("primary-button");
Button secondaryBtn = new Button("Secondary");
secondaryBtn.getStyleClass().add("secondary-button");
Button successBtn = new Button("Success");
successBtn.getStyleClass().add("success-button");
Button dangerBtn = new Button("Danger");
dangerBtn.getStyleClass().add("danger-button");
// Toggle buttons
ToggleButton toggleBtn = new ToggleButton("Toggle Me");
toggleBtn.selectedProperty().addListener((obs, oldVal, newVal) -> {
System.out.println("Toggle button: " + newVal);
});
buttonBox.getChildren().addAll(primaryBtn, secondaryBtn, successBtn, dangerBtn, toggleBtn);
// Text Input
VBox inputBox = new VBox(10);
Label inputLabel = new Label("Text Input:");
TextField textField = new TextField();
textField.setPromptText("Enter text here...");
textField.setPrefWidth(300);
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("Enter password...");
passwordField.setPrefWidth(300);
TextArea textArea = new TextArea();
textArea.setPromptText("Multi-line text here...");
textArea.setPrefRowCount(4);
textArea.setPrefWidth(300);
inputBox.getChildren().addAll(inputLabel, textField, passwordField, textArea);
// Progress indicators
VBox progressBox = new VBox(10);
Label progressLabel = new Label("Progress Indicators:");
ProgressBar progressBar = new ProgressBar(0.5);
ProgressIndicator progressIndicator = new ProgressIndicator(0.7);
Slider slider = new Slider(0, 100, 50);
progressBox.getChildren().addAll(progressLabel, progressBar, progressIndicator, slider);
vbox.getChildren().addAll(title, buttonBox, inputBox, progressBox);
return vbox;
}
private VBox createSelectionControlsPane() {
VBox vbox = new VBox(15);
vbox.setPadding(new Insets(20));
// Checkboxes
VBox checkboxBox = new VBox(10);
Label checkboxLabel = new Label("Checkboxes:");
CheckBox option1 = new CheckBox("Option 1");
CheckBox option2 = new CheckBox("Option 2");
CheckBox option3 = new CheckBox("Option 3");
// Three-state checkbox
CheckBox indeterminateCheck = new CheckBox("Allow Indeterminate");
indeterminateCheck.setAllowIndeterminate(true);
indeterminateCheck.setIndeterminate(true);
checkboxBox.getChildren().addAll(checkboxLabel, option1, option2, option3, indeterminateCheck);
// Radio buttons
VBox radioBox = new VBox(10);
Label radioLabel = new Label("Radio Buttons:");
ToggleGroup group = new ToggleGroup();
RadioButton radio1 = new RadioButton("Choice A");
radio1.setToggleGroup(group);
RadioButton radio2 = new RadioButton("Choice B");
radio2.setToggleGroup(group);
RadioButton radio3 = new RadioButton("Choice C");
radio3.setToggleGroup(group);
// Select first radio button
radio1.setSelected(true);
radioBox.getChildren().addAll(radioLabel, radio1, radio2, radio3);
// ComboBox
VBox comboBox = new VBox(10);
Label comboLabel = new Label("ComboBox:");
ObservableList options = FXCollections.observableArrayList(
"Java", "Python", "JavaScript", "C++", "C#", "Go", "Rust"
);
ComboBox languageCombo = new ComboBox<>(options);
languageCombo.setPromptText("Select a language");
languageCombo.setPrefWidth(200);
// Display selected value
Label selectedLabel = new Label("Selected: None");
languageCombo.valueProperty().addListener((obs, oldVal, newVal) -> {
selectedLabel.setText("Selected: " + newVal);
});
comboBox.getChildren().addAll(comboLabel, languageCombo, selectedLabel);
// ListView
VBox listBox = new VBox(10);
Label listLabel = new Label("ListView:");
ObservableList items = FXCollections.observableArrayList(
"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"
);
ListView listView = new ListView<>(items);
listView.setPrefHeight(150);
listView.setPrefWidth(200);
// Multiple selection
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
listBox.getChildren().addAll(listLabel, listView);
vbox.getChildren().addAll(checkboxBox, radioBox, comboBox, listBox);
return vbox;
}
private VBox createDataEntryPane() {
VBox vbox = new VBox(15);
vbox.setPadding(new Insets(20));
// Date Picker
VBox dateBox = new VBox(10);
Label dateLabel = new Label("Date Picker:");
DatePicker datePicker = new DatePicker(LocalDate.now());
datePicker.setPrefWidth(200);
datePicker.valueProperty().addListener((obs, oldDate, newDate) -> {
System.out.println("Selected date: " + newDate);
});
dateBox.getChildren().addAll(dateLabel, datePicker);
// Color Picker
VBox colorBox = new VBox(10);
Label colorLabel = new Label("Color Picker:");
ColorPicker colorPicker = new ColorPicker(Color.BLUE);
colorPicker.setPrefWidth(200);
// Preview circle
Circle colorPreview = new Circle(20);
colorPreview.setFill(Color.BLUE);
colorPicker.valueProperty().addListener((obs, oldColor, newColor) -> {
colorPreview.setFill(newColor);
});
HBox colorPreviewBox = new HBox(10, colorPicker, colorPreview);
colorPreviewBox.setAlignment(Pos.CENTER_LEFT);
colorBox.getChildren().addAll(colorLabel, colorPreviewBox);
// Spinner
VBox spinnerBox = new VBox(10);
Label spinnerLabel = new Label("Spinners:");
Spinner intSpinner = new Spinner<>(1, 100, 50);
intSpinner.setPrefWidth(150);
Spinner doubleSpinner = new Spinner<>(0.0, 10.0, 5.0, 0.5);
doubleSpinner.setPrefWidth(150);
HBox spinnerHBox = new HBox(20, intSpinner, doubleSpinner);
spinnerBox.getChildren().addAll(spinnerLabel, spinnerHBox);
// TableView
VBox tableBox = new VBox(10);
Label tableLabel = new Label("TableView:");
TableView tableView = new TableView<>();
tableView.setPrefHeight(200);
// Define columns
TableColumn nameColumn = new TableColumn<>("Name");
nameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
TableColumn ageColumn = new TableColumn<>("Age");
ageColumn.setCellValueFactory(cellData -> cellData.getValue().ageProperty().asObject());
TableColumn emailColumn = new TableColumn<>("Email");
emailColumn.setCellValueFactory(cellData -> cellData.getValue().emailProperty());
tableView.getColumns().addAll(nameColumn, ageColumn, emailColumn);
// Add sample data
ObservableList people = FXCollections.observableArrayList(
new Person("Alice Johnson", 28, "alice@example.com"),
new Person("Bob Smith", 35, "bob@example.com"),
new Person("Charlie Brown", 42, "charlie@example.com"),
new Person("Diana Prince", 30, "diana@example.com")
);
tableView.setItems(people);
tableBox.getChildren().addAll(tableLabel, tableView);
vbox.getChildren().addAll(dateBox, colorBox, spinnerBox, tableBox);
return vbox;
}
private VBox createAdvancedControlsPane() {
VBox vbox = new VBox(15);
vbox.setPadding(new Insets(20));
// TreeView
VBox treeBox = new VBox(10);
Label treeLabel = new Label("TreeView:");
TreeItem rootItem = new TreeItem<>("Projects");
rootItem.setExpanded(true);
TreeItem javaProjects = new TreeItem<>("Java Projects");
javaProjects.getChildren().addAll(
new TreeItem<>("Inventory System"),
new TreeItem<>("E-commerce Platform"),
new TreeItem<>("Banking Application")
);
TreeItem webProjects = new TreeItem<>("Web Projects");
webProjects.getChildren().addAll(
new TreeItem<>("React Dashboard"),
new TreeItem<>("Angular E-commerce"),
new TreeItem<>("Vue.js Admin Panel")
);
rootItem.getChildren().addAll(javaProjects, webProjects);
TreeView treeView = new TreeView<>(rootItem);
treeView.setPrefHeight(200);
treeBox.getChildren().addAll(treeLabel, treeView);
// Accordion with TitledPane
VBox accordionBox = new VBox(10);
Label accordionLabel = new Label("Accordion:");
TitledPane pane1 = new TitledPane("General Settings",
new VBox(10, new CheckBox("Auto-update"), new CheckBox("Notifications")));
TitledPane pane2 = new TitledPane("Privacy",
new VBox(10, new CheckBox("Collect usage data"), new CheckBox("Share analytics")));
TitledPane pane3 = new TitledPane("Advanced",
new VBox(10, new TextField("API Key"), new Button("Validate")));
Accordion accordion = new Accordion(pane1, pane2, pane3);
accordion.setPrefHeight(200);
accordionBox.getChildren().addAll(accordionLabel, accordion);
// WebView
VBox webBox = new VBox(10);
Label webLabel = new Label("WebView (Embedded Browser):");
// Note: WebView requires JavaFX WebKit module
// WebView webView = new WebView();
// webView.getEngine().load("https://openjfx.io");
// webView.setPrefHeight(200);
Label webViewNote = new Label("Note: WebView requires JavaFX WebKit module");
webViewNote.setStyle("-fx-font-style: italic; -fx-text-fill: #666;");
webBox.getChildren().addAll(webLabel, webViewNote);
vbox.getChildren().addAll(treeBox, accordionBox, webBox);
return vbox;
}
// Model class for TableView
public static class Person {
private final StringProperty name = new SimpleStringProperty();
private final IntegerProperty age = new SimpleIntegerProperty();
private final StringProperty email = new SimpleStringProperty();
public Person(String name, int age, String email) {
this.name.set(name);
this.age.set(age);
this.email.set(email);
}
public StringProperty nameProperty() { return name; }
public IntegerProperty ageProperty() { return age; }
public StringProperty emailProperty() { return email; }
}
public static void main(String[] args) {
launch(args);
}
}
| Control Category | Key Controls | Description | Common Use |
|---|---|---|---|
| Basic | Label, Button, TextField, TextArea | Fundamental UI elements | Forms, navigation, input |
| Selection | CheckBox, RadioButton, ComboBox, ListView | Single/multiple selection | Options, preferences, lists |
| Data Entry | DatePicker, ColorPicker, Spinner, TableView | Specialized data input | Forms, data management |
| Containers | TabPane, Accordion, TitledPane, SplitPane | Organize and group controls | Complex layouts, navigation |
| Advanced | TreeView, WebView, HTMLEditor, ProgressBar | Specialized functionality | Browsers, editors, progress |
4. JavaFX CSS Styling
JavaFX supports CSS (Cascading Style Sheets) for styling UI components. Unlike web CSS, JavaFX CSS has its own properties and selectors optimized for desktop applications.
/* JavaFX CSS - Modern Application Styles */
/* === Root Styles & Variables === */
.root {
-fx-primary-color: #3D8B37;
-fx-secondary-color: #FF6B35;
-fx-accent-color: #00A8E8;
-fx-light-color: #F8F9FA;
-fx-dark-color: #343A40;
-fx-success-color: #28A745;
-fx-danger-color: #DC3545;
-fx-warning-color: #FFC107;
-fx-info-color: #17A2B8;
/* Font variables */
-fx-font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif;
-fx-font-size: 14px;
/* Background gradient */
-fx-background-color: linear-gradient(to bottom right, #f8f9fa, #e9ecef);
}
/* === Typography === */
.label {
-fx-text-fill: -fx-dark-color;
}
.label.heading {
-fx-font-size: 24px;
-fx-font-weight: bold;
-fx-text-fill: -fx-primary-color;
}
.label.subheading {
-fx-font-size: 18px;
-fx-font-weight: 600;
-fx-text-fill: #666;
}
.label.caption {
-fx-font-size: 12px;
-fx-text-fill: #888;
-fx-font-style: italic;
}
/* === Button Styles === */
.button {
-fx-font-weight: 600;
-fx-font-size: 14px;
-fx-padding: 10px 20px;
-fx-background-radius: 5px;
-fx-border-radius: 5px;
-fx-cursor: hand;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 4, 0, 0, 2);
-fx-transition: all 0.3s;
}
.button:hover {
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 6, 0, 0, 3);
-fx-translate-y: -1px;
}
.button:pressed {
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 2, 0, 0, 1);
-fx-translate-y: 0px;
}
.primary-button {
-fx-background-color: -fx-primary-color;
-fx-text-fill: white;
}
.primary-button:hover {
-fx-background-color: derive(-fx-primary-color, -10%);
}
.secondary-button {
-fx-background-color: -fx-secondary-color;
-fx-text-fill: white;
}
.secondary-button:hover {
-fx-background-color: derive(-fx-secondary-color, -10%);
}
.success-button {
-fx-background-color: -fx-success-color;
-fx-text-fill: white;
}
.danger-button {
-fx-background-color: -fx-danger-color;
-fx-text-fill: white;
}
.outline-button {
-fx-background-color: transparent;
-fx-border-color: -fx-primary-color;
-fx-border-width: 2px;
-fx-text-fill: -fx-primary-color;
}
.outline-button:hover {
-fx-background-color: -fx-primary-color;
-fx-text-fill: white;
}
/* === Text Input Styles === */
.text-field, .password-field, .text-area {
-fx-background-color: white;
-fx-border-color: #ddd;
-fx-border-width: 1px;
-fx-border-radius: 4px;
-fx-padding: 10px;
-fx-font-size: 14px;
}
.text-field:focused, .password-field:focused, .text-area:focused {
-fx-border-color: -fx-primary-color;
-fx-border-width: 2px;
-fx-effect: dropshadow(gaussian, rgba(61, 139, 55, 0.2), 10, 0, 0, 0);
}
.text-field.error, .password-field.error {
-fx-border-color: -fx-danger-color;
-fx-border-width: 2px;
}
.text-area .content {
-fx-background-color: white;
-fx-background-radius: 4px;
}
/* === ComboBox Styles === */
.combo-box {
-fx-background-color: white;
-fx-border-color: #ddd;
-fx-border-width: 1px;
-fx-border-radius: 4px;
-fx-padding: 5px;
}
.combo-box .arrow-button {
-fx-background-color: transparent;
}
.combo-box .list-cell {
-fx-padding: 8px;
}
.combo-box-popup .list-view {
-fx-background-color: white;
-fx-border-color: #ddd;
-fx-border-width: 1px;
}
.combo-box-popup .list-cell {
-fx-padding: 8px 12px;
}
.combo-box-popup .list-cell:hover {
-fx-background-color: -fx-light-color;
}
.combo-box-popup .list-cell:selected {
-fx-background-color: -fx-primary-color;
-fx-text-fill: white;
}
/* === CheckBox & RadioButton === */
.check-box, .radio-button {
-fx-text-fill: -fx-dark-color;
-fx-font-size: 14px;
}
.check-box .box, .radio-button .radio {
-fx-background-color: white;
-fx-border-color: #ccc;
-fx-border-width: 2px;
-fx-border-radius: 3px;
}
.check-box:selected .mark, .radio-button:selected .dot {
-fx-background-color: -fx-primary-color;
}
.check-box:hover .box, .radio-button:hover .radio {
-fx-border-color: -fx-primary-color;
}
/* === Slider & Progress === */
.slider .track {
-fx-background-color: #e9ecef;
-fx-background-radius: 2px;
}
.slider .thumb {
-fx-background-color: -fx-primary-color;
-fx-background-radius: 50%;
-fx-pref-width: 20px;
-fx-pref-height: 20px;
}
.progress-bar .track {
-fx-background-color: #e9ecef;
-fx-background-radius: 3px;
}
.progress-bar .bar {
-fx-background-color: -fx-primary-color;
-fx-background-radius: 3px;
-fx-padding: 4px;
}
.progress-indicator .percentage {
-fx-fill: -fx-dark-color;
}
/* === TableView Styles === */
.table-view {
-fx-background-color: white;
-fx-border-color: #ddd;
-fx-border-width: 1px;
-fx-border-radius: 4px;
}
.table-view .column-header {
-fx-background-color: -fx-light-color;
-fx-border-color: #ddd;
-fx-border-width: 0 0 1px 0;
-fx-font-weight: bold;
-fx-text-fill: -fx-dark-color;
}
.table-view .table-row-cell {
-fx-border-color: #f0f0f0;
-fx-border-width: 0 0 1px 0;
}
.table-view .table-row-cell:even {
-fx-background-color: #fafafa;
}
.table-view .table-row-cell:odd {
-fx-background-color: white;
}
.table-view .table-row-cell:hover {
-fx-background-color: #f0f8ff;
}
.table-view .table-row-cell:selected {
-fx-background-color: -fx-primary-color;
-fx-text-fill: white;
}
/* === TabPane Styles === */
.tab-pane {
-fx-background-color: white;
}
.tab-pane .tab-header-area {
-fx-background-color: -fx-light-color;
}
.tab-pane .tab {
-fx-background-color: transparent;
-fx-border-color: transparent;
-fx-padding: 10px 20px;
-fx-font-weight: 600;
}
.tab-pane .tab:selected {
-fx-background-color: white;
-fx-border-color: transparent transparent -fx-primary-color transparent;
-fx-border-width: 0 0 3px 0;
}
.tab-pane .tab:hover {
-fx-background-color: rgba(255, 255, 255, 0.5);
}
/* === ScrollBar Styles === */
.scroll-bar {
-fx-background-color: transparent;
}
.scroll-bar .thumb {
-fx-background-color: #ccc;
-fx-background-radius: 4px;
}
.scroll-bar .thumb:hover {
-fx-background-color: #999;
}
.scroll-bar .increment-button, .scroll-bar .decrement-button {
-fx-background-color: transparent;
-fx-padding: 0;
}
/* === Custom Components === */
.card {
-fx-background-color: white;
-fx-background-radius: 8px;
-fx-border-color: #e9ecef;
-fx-border-width: 1px;
-fx-border-radius: 8px;
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.08), 10, 0, 0, 2);
-fx-padding: 20px;
}
.card:hover {
-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.12), 12, 0, 0, 3);
}
.alert-box {
-fx-background-color: #fff3cd;
-fx-border-color: #ffeaa7;
-fx-border-width: 1px;
-fx-border-radius: 4px;
-fx-padding: 15px;
}
.alert-box.success {
-fx-background-color: #d4edda;
-fx-border-color: #c3e6cb;
}
.alert-box.error {
-fx-background-color: #f8d7da;
-fx-border-color: #f5c6cb;
}
/* === Animations === */
@keyframes fadeIn {
from { -fx-opacity: 0; }
to { -fx-opacity: 1; }
}
@keyframes slideIn {
from { -fx-translate-x: -20px; -fx-opacity: 0; }
to { -fx-translate-x: 0; -fx-opacity: 1; }
}
.fade-in {
-fx-animation: fadeIn 0.5s;
}
.slide-in {
-fx-animation: slideIn 0.3s;
}
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class CSSExample extends Application {
@Override
public void start(Stage primaryStage) {
// Main container
VBox root = new VBox(20);
root.setPadding(new Insets(30));
root.setAlignment(Pos.TOP_CENTER);
// Title
Label title = new Label("JavaFX CSS Styling Demo");
title.getStyleClass().add("heading");
// Card 1: Form
VBox formCard = createFormCard();
formCard.getStyleClass().add("card");
// Card 2: Dashboard
VBox dashboardCard = createDashboardCard();
dashboardCard.getStyleClass().add("card");
// Card 3: Alerts
VBox alertsCard = createAlertsCard();
alertsCard.getStyleClass().add("card");
root.getChildren().addAll(title, formCard, dashboardCard, alertsCard);
// Create scene and apply CSS
Scene scene = new Scene(root, 900, 700);
scene.getStylesheets().add(getClass().getResource("styles.css").toExternalForm());
// Configure stage
primaryStage.setTitle("JavaFX CSS Styling Example");
primaryStage.setScene(scene);
primaryStage.show();
}
private VBox createFormCard() {
VBox card = new VBox(15);
card.setPadding(new Insets(20));
Label cardTitle = new Label("User Registration Form");
cardTitle.getStyleClass().add("subheading");
// Form grid
GridPane formGrid = new GridPane();
formGrid.setHgap(15);
formGrid.setVgap(10);
// Row 1: Name
formGrid.add(new Label("Full Name:"), 0, 0);
TextField nameField = new TextField();
nameField.setPromptText("Enter your full name");
formGrid.add(nameField, 1, 0);
// Row 2: Email
formGrid.add(new Label("Email:"), 0, 1);
TextField emailField = new TextField();
emailField.setPromptText("example@domain.com");
formGrid.add(emailField, 1, 1);
// Row 3: Password
formGrid.add(new Label("Password:"), 0, 2);
PasswordField passwordField = new PasswordField();
passwordField.setPromptText("Minimum 8 characters");
formGrid.add(passwordField, 1, 2);
// Row 4: Country
formGrid.add(new Label("Country:"), 0, 3);
ComboBox countryCombo = new ComboBox<>();
countryCombo.getItems().addAll("United States", "Canada", "United Kingdom", "Australia", "Germany", "Japan");
countryCombo.setPromptText("Select your country");
formGrid.add(countryCombo, 1, 3);
// Row 5: Newsletter
CheckBox newsletterCheck = new CheckBox("Subscribe to newsletter");
formGrid.add(newletterCheck, 1, 4);
// Buttons
HBox buttonBox = new HBox(10);
buttonBox.setAlignment(Pos.CENTER_RIGHT);
Button cancelBtn = new Button("Cancel");
cancelBtn.getStyleClass().add("outline-button");
Button submitBtn = new Button("Register");
submitBtn.getStyleClass().add("primary-button");
buttonBox.getChildren().addAll(cancelBtn, submitBtn);
card.getChildren().addAll(cardTitle, formGrid, buttonBox);
return card;
}
private VBox createDashboardCard() {
VBox card = new VBox(15);
card.setPadding(new Insets(20));
Label cardTitle = new Label("Dashboard Metrics");
cardTitle.getStyleClass().add("subheading");
// Metrics grid
GridPane metricsGrid = new GridPane();
metricsGrid.setHgap(20);
metricsGrid.setVgap(15);
metricsGrid.setAlignment(Pos.CENTER);
// Metric 1: Users
VBox usersMetric = createMetricBox("Total Users", "1,245", "+12%", "success-button");
metricsGrid.add(usersMetric, 0, 0);
// Metric 2: Revenue
VBox revenueMetric = createMetricBox("Monthly Revenue", "$45,230", "+8%", "primary-button");
metricsGrid.add(revenueMetric, 1, 0);
// Metric 3: Conversion
VBox conversionMetric = createMetricBox("Conversion Rate", "3.2%", "-2%", "danger-button");
metricsGrid.add(conversionMetric, 2, 0);
// Metric 4: Active
VBox activeMetric = createMetricBox("Active Sessions", "324", "+5%", "secondary-button");
metricsGrid.add(activeMetric, 3, 0);
// Progress section
VBox progressBox = new VBox(10);
progressBox.setPadding(new Insets(20, 0, 0, 0));
Label progressLabel = new Label("Storage Usage");
progressLabel.getStyleClass().add("subheading");
ProgressBar storageBar = new ProgressBar(0.65);
storageBar.setPrefWidth(300);
Label storageText = new Label("65% used (325GB of 500GB)");
storageText.getStyleClass().add("caption");
progressBox.getChildren().addAll(progressLabel, storageBar, storageText);
card.getChildren().addAll(cardTitle, metricsGrid, progressBox);
return card;
}
private VBox createMetricBox(String title, String value, String change, String styleClass) {
VBox metricBox = new VBox(5);
metricBox.setAlignment(Pos.CENTER);
metricBox.setPadding(new Insets(15));
metricBox.setStyle("-fx-background-color: #f8f9fa; -fx-border-radius: 8px;");
Label titleLabel = new Label(title);
titleLabel.getStyleClass().add("caption");
Label valueLabel = new Label(value);
valueLabel.setStyle("-fx-font-size: 24px; -fx-font-weight: bold;");
Button changeBtn = new Button(change);
changeBtn.getStyleClass().add(styleClass);
changeBtn.setStyle("-fx-padding: 5px 10px; -fx-font-size: 12px;");
metricBox.getChildren().addAll(titleLabel, valueLabel, changeBtn);
return metricBox;
}
private VBox createAlertsCard() {
VBox card = new VBox(15);
card.setPadding(new Insets(20));
Label cardTitle = new Label("System Alerts & Notifications");
cardTitle.getStyleClass().add("subheading");
// Success alert
HBox successAlert = createAlert("success", "✓", "System update completed successfully", "Just now");
// Error alert
HBox errorAlert = createAlert("error", "⚠", "Database connection failed. Retrying...", "5 minutes ago");
// Warning alert
HBox warningAlert = createAlert("warning", "!", "Storage space running low", "1 hour ago");
// Info alert
HBox infoAlert = createAlert("info", "ℹ", "New feature available: Dark mode", "2 hours ago");
card.getChildren().addAll(cardTitle, successAlert, errorAlert, warningAlert, infoAlert);
return card;
}
private HBox createAlert(String type, String icon, String message, String time) {
HBox alertBox = new HBox(10);
alertBox.setPadding(new Insets(12));
alertBox.setAlignment(Pos.CENTER_LEFT);
alertBox.getStyleClass().add("alert-box");
if ("success".equals(type)) {
alertBox.getStyleClass().add("success");
} else if ("error".equals(type)) {
alertBox.getStyleClass().add("error");
}
Label iconLabel = new Label(icon);
iconLabel.setStyle("-fx-font-size: 18px; -fx-font-weight: bold;");
VBox messageBox = new VBox(2);
Label messageLabel = new Label(message);
Label timeLabel = new Label(time);
timeLabel.getStyleClass().add("caption");
messageBox.getChildren().addAll(messageLabel, timeLabel);
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
Button dismissBtn = new Button("Dismiss");
dismissBtn.getStyleClass().add("outline-button");
dismissBtn.setStyle("-fx-padding: 5px 15px; -fx-font-size: 12px;");
alertBox.getChildren().addAll(iconLabel, messageBox, spacer, dismissBtn);
return alertBox;
}
public static void main(String[] args) {
launch(args);
}
}
5. Animations & Visual Effects
JavaFX provides a comprehensive animation API and built-in visual effects for creating engaging, modern user interfaces.
import javafx.animation.*;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.effect.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimationsDemo extends Application {
private Timeline pulseTimeline;
private ParallelTransition parallelTransition;
private SequentialTransition sequentialTransition;
@Override
public void start(Stage primaryStage) {
// Main layout
BorderPane root = new BorderPane();
root.setStyle("-fx-background-color: linear-gradient(to bottom right, #1a2a3a, #2c3e50);");
// Top: Controls
VBox controls = createControls();
root.setTop(controls);
// Center: Animation Canvas
StackPane animationCanvas = createAnimationCanvas();
root.setCenter(animationCanvas);
// Bottom: Status
HBox statusBar = createStatusBar();
root.setBottom(statusBar);
// Create scene
Scene scene = new Scene(root, 1000, 700);
// Configure stage
primaryStage.setTitle("JavaFX Animations & Effects Demo");
primaryStage.setScene(scene);
primaryStage.show();
// Start initial animations
startInitialAnimations();
}
private VBox createControls() {
VBox controls = new VBox(15);
controls.setPadding(new Insets(20));
controls.setStyle("-fx-background-color: rgba(255, 255, 255, 0.1);");
Label title = new Label("Animation Controls");
title.setFont(Font.font(24));
title.setTextFill(Color.WHITE);
// Animation type selector
HBox typeBox = new HBox(10);
typeBox.setAlignment(Pos.CENTER_LEFT);
Label typeLabel = new Label("Animation Type:");
typeLabel.setTextFill(Color.WHITE);
ComboBox animationType = new ComboBox<>();
animationType.getItems().addAll(
"Fade", "Translate", "Scale", "Rotate", "Path", "Parallel", "Sequential", "Pulse"
);
animationType.setValue("Fade");
animationType.setPrefWidth(150);
typeBox.getChildren().addAll(typeLabel, animationType);
// Control buttons
HBox buttonBox = new HBox(10);
Button playBtn = createStyledButton("▶ Play", "#3D8B37");
Button pauseBtn = createStyledButton("⏸ Pause", "#FF6B35");
Button stopBtn = createStyledButton("⏹ Stop", "#DC3545");
Button resetBtn = createStyledButton("↺ Reset", "#00A8E8");
buttonBox.getChildren().addAll(playBtn, pauseBtn, stopBtn, resetBtn);
// Effect selectors
HBox effectBox = new HBox(10);
effectBox.setAlignment(Pos.CENTER_LEFT);
Label effectLabel = new Label("Visual Effects:");
effectLabel.setTextFill(Color.WHITE);
CheckBox glowCheck = new CheckBox("Glow");
glowCheck.setTextFill(Color.WHITE);
CheckBox shadowCheck = new CheckBox("Drop Shadow");
shadowCheck.setTextFill(Color.WHITE);
CheckBox blurCheck = new CheckBox("Blur");
blurCheck.setTextFill(Color.WHITE);
CheckBox reflectionCheck = new CheckBox("Reflection");
reflectionCheck.setTextFill(Color.WHITE);
effectBox.getChildren().addAll(effectLabel, glowCheck, shadowCheck, blurCheck, reflectionCheck);
// Event handlers
playBtn.setOnAction(e -> playAnimation(animationType.getValue()));
pauseBtn.setOnAction(e -> pauseAnimation());
stopBtn.setOnAction(e -> stopAnimation());
resetBtn.setOnAction(e -> resetAnimation());
// Effect handlers
glowCheck.selectedProperty().addListener((obs, oldVal, newVal) ->
applyEffect("glow", newVal));
shadowCheck.selectedProperty().addListener((obs, oldVal, newVal) ->
applyEffect("shadow", newVal));
blurCheck.selectedProperty().addListener((obs, oldVal, newVal) ->
applyEffect("blur", newVal));
reflectionCheck.selectedProperty().addListener((obs, oldVal, newVal) ->
applyEffect("reflection", newVal));
controls.getChildren().addAll(title, typeBox, buttonBox, effectBox);
return controls;
}
private StackPane createAnimationCanvas() {
StackPane canvas = new StackPane();
canvas.setPadding(new Insets(20));
// Create animated shapes
Rectangle rect = new Rectangle(100, 100, Color.web("#3D8B37", 0.8));
rect.setArcWidth(20);
rect.setArcHeight(20);
rect.setLayoutX(-150);
Circle circle = new Circle(50, Color.web("#FF6B35", 0.8));
circle.setLayoutX(0);
circle.setLayoutY(0);
Polygon triangle = new Polygon(0, -50, 50, 50, -50, 50);
triangle.setFill(Color.web("#00A8E8", 0.8));
triangle.setLayoutX(150);
// Text label
Label animatedText = new Label("JavaFX Animations");
animatedText.setFont(Font.font(28));
animatedText.setTextFill(Color.WHITE);
animatedText.setLayoutY(-100);
// Store references for animation
canvas.getProperties().put("rect", rect);
canvas.getProperties().put("circle", circle);
canvas.getProperties().put("triangle", triangle);
canvas.getProperties().put("text", animatedText);
canvas.getChildren().addAll(rect, circle, triangle, animatedText);
return canvas;
}
private HBox createStatusBar() {
HBox statusBar = new HBox(10);
statusBar.setPadding(new Insets(10));
statusBar.setStyle("-fx-background-color: rgba(0, 0, 0, 0.2);");
statusBar.setAlignment(Pos.CENTER);
Label statusLabel = new Label("Ready");
statusLabel.setTextFill(Color.WHITE);
ProgressBar animationProgress = new ProgressBar(0);
animationProgress.setPrefWidth(200);
statusBar.getChildren().addAll(statusLabel, animationProgress);
return statusBar;
}
private Button createStyledButton(String text, String color) {
Button btn = new Button(text);
btn.setStyle("-fx-background-color: " + color +
"; -fx-text-fill: white; -fx-font-weight: bold;" +
" -fx-padding: 10px 20px; -fx-background-radius: 5px;");
return btn;
}
private void startInitialAnimations() {
// Start a gentle pulsing animation on the circle
Circle circle = (Circle) ((StackPane) ((BorderPane)
((Stage) Stage.getWindows().get(0)).getScene().getRoot())
.getCenter()).getProperties().get("circle");
pulseTimeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(circle.radiusProperty(), 50),
new KeyValue(circle.opacityProperty(), 0.8)),
new KeyFrame(Duration.seconds(1),
new KeyValue(circle.radiusProperty(), 60),
new KeyValue(circle.opacityProperty(), 0.6)),
new KeyFrame(Duration.seconds(2),
new KeyValue(circle.radiusProperty(), 50),
new KeyValue(circle.opacityProperty(), 0.8))
);
pulseTimeline.setCycleCount(Timeline.INDEFINITE);
pulseTimeline.setAutoReverse(true);
pulseTimeline.play();
}
private void playAnimation(String type) {
stopAnimation(); // Stop any ongoing animations
StackPane canvas = (StackPane) ((BorderPane)
((Stage) Stage.getWindows().get(0)).getScene().getRoot())
.getCenter();
Rectangle rect = (Rectangle) canvas.getProperties().get("rect");
Circle circle = (Circle) canvas.getProperties().get("circle");
Polygon triangle = (Polygon) canvas.getProperties().get("triangle");
Label text = (Label) canvas.getProperties().get("text");
switch (type) {
case "Fade":
FadeTransition fade = new FadeTransition(Duration.seconds(2), rect);
fade.setFromValue(1.0);
fade.setToValue(0.2);
fade.setCycleCount(2);
fade.setAutoReverse(true);
fade.play();
break;
case "Translate":
TranslateTransition translate = new TranslateTransition(Duration.seconds(2), circle);
translate.setFromX(0);
translate.setToX(200);
translate.setCycleCount(2);
translate.setAutoReverse(true);
translate.play();
break;
case "Scale":
ScaleTransition scale = new ScaleTransition(Duration.seconds(2), triangle);
scale.setFromX(1);
scale.setFromY(1);
scale.setToX(1.5);
scale.setToY(1.5);
scale.setCycleCount(2);
scale.setAutoReverse(true);
scale.play();
break;
case "Rotate":
RotateTransition rotate = new RotateTransition(Duration.seconds(3), rect);
rotate.setByAngle(360);
rotate.setCycleCount(2);
rotate.setAutoReverse(true);
rotate.play();
break;
case "Path":
Path path = new Path();
path.getElements().add(new MoveTo(0, 0));
path.getElements().add(new CubicCurveTo(50, -100, 150, 100, 200, 0));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(3));
pathTransition.setPath(path);
pathTransition.setNode(circle);
pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setCycleCount(2);
pathTransition.setAutoReverse(true);
pathTransition.play();
break;
case "Parallel":
FadeTransition fadeText = new FadeTransition(Duration.seconds(2), text);
fadeText.setFromValue(0.2);
fadeText.setToValue(1.0);
TranslateTransition translateText = new TranslateTransition(Duration.seconds(2), text);
translateText.setFromY(-100);
translateText.setToY(0);
parallelTransition = new ParallelTransition(fadeText, translateText);
parallelTransition.setCycleCount(2);
parallelTransition.setAutoReverse(true);
parallelTransition.play();
break;
case "Sequential":
FadeTransition fade1 = new FadeTransition(Duration.seconds(1), rect);
fade1.setFromValue(1.0);
fade1.setToValue(0.2);
FadeTransition fade2 = new FadeTransition(Duration.seconds(1), circle);
fade2.setFromValue(1.0);
fade2.setToValue(0.2);
FadeTransition fade3 = new FadeTransition(Duration.seconds(1), triangle);
fade3.setFromValue(1.0);
fade3.setToValue(0.2);
sequentialTransition = new SequentialTransition(fade1, fade2, fade3);
sequentialTransition.setCycleCount(2);
sequentialTransition.setAutoReverse(true);
sequentialTransition.play();
break;
case "Pulse":
Timeline pulse = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(circle.fillProperty(), Color.web("#FF6B35", 0.8))),
new KeyFrame(Duration.seconds(0.5),
new KeyValue(circle.fillProperty(), Color.web("#FF8B5D", 1.0))),
new KeyFrame(Duration.seconds(1),
new KeyValue(circle.fillProperty(), Color.web("#FF6B35", 0.8)))
);
pulse.setCycleCount(Timeline.INDEFINITE);
pulse.play();
break;
}
}
private void pauseAnimation() {
if (pulseTimeline != null) {
pulseTimeline.pause();
}
if (parallelTransition != null) {
parallelTransition.pause();
}
if (sequentialTransition != null) {
sequentialTransition.pause();
}
}
private void stopAnimation() {
if (pulseTimeline != null) {
pulseTimeline.stop();
}
if (parallelTransition != null) {
parallelTransition.stop();
}
if (sequentialTransition != null) {
sequentialTransition.stop();
}
}
private void resetAnimation() {
stopAnimation();
StackPane canvas = (StackPane) ((BorderPane)
((Stage) Stage.getWindows().get(0)).getScene().getRoot())
.getCenter();
Rectangle rect = (Rectangle) canvas.getProperties().get("rect");
Circle circle = (Circle) canvas.getProperties().get("circle");
Polygon triangle = (Polygon) canvas.getProperties().get("triangle");
Label text = (Label) canvas.getProperties().get("text");
// Reset positions and properties
rect.setLayoutX(-150);
rect.setLayoutY(0);
rect.setRotate(0);
rect.setScaleX(1);
rect.setScaleY(1);
rect.setOpacity(0.8);
rect.setEffect(null);
circle.setLayoutX(0);
circle.setLayoutY(0);
circle.setTranslateX(0);
circle.setTranslateY(0);
circle.setRotate(0);
circle.setScaleX(1);
circle.setScaleY(1);
circle.setOpacity(0.8);
circle.setEffect(null);
circle.setFill(Color.web("#FF6B35", 0.8));
triangle.setLayoutX(150);
triangle.setLayoutY(0);
triangle.setRotate(0);
triangle.setScaleX(1);
triangle.setScaleY(1);
triangle.setOpacity(0.8);
triangle.setEffect(null);
text.setLayoutY(-100);
text.setTranslateY(0);
text.setOpacity(1.0);
text.setEffect(null);
// Restart pulse animation
if (pulseTimeline != null) {
pulseTimeline.play();
}
}
private void applyEffect(String effectType, boolean apply) {
StackPane canvas = (StackPane) ((BorderPane)
((Stage) Stage.getWindows().get(0)).getScene().getRoot())
.getCenter();
Circle circle = (Circle) canvas.getProperties().get("circle");
if (apply) {
switch (effectType) {
case "glow":
circle.setEffect(new Glow(0.8));
break;
case "shadow":
circle.setEffect(new DropShadow(10, Color.BLACK));
break;
case "blur":
circle.setEffect(new GaussianBlur(10));
break;
case "reflection":
Reflection reflection = new Reflection();
reflection.setFraction(0.7);
circle.setEffect(reflection);
break;
}
} else {
circle.setEffect(null);
}
}
public static void main(String[] args) {
launch(args);
}
}
Animation Types
- Timeline: Keyframe-based animations
- Transition: Predefined animations
- ParallelTransition: Multiple animations simultaneously
- SequentialTransition: Animations in sequence
- PathTransition: Follow a path
- Fade/Rotate/Scale/Translate: Specific transitions
Visual Effects
- DropShadow: Cast shadow effect
- InnerShadow: Inner shadow effect
- Glow: Glowing edges
- Bloom: Bright blooming effect
- GaussianBlur: Blur effect
- Reflection: Mirror reflection
- Lighting: 3D lighting effect
6. Charts & 3D Graphics
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Point3D;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import java.util.Random;
public class Charts3DDemo extends Application {
private PerspectiveCamera camera;
private Rotate rotateX = new Rotate(0, Rotate.X_AXIS);
private Rotate rotateY = new Rotate(0, Rotate.Y_AXIS);
private double mousePosX, mousePosY;
@Override
public void start(Stage primaryStage) {
// Main layout with tabs
TabPane tabPane = new TabPane();
// Tab 1: 2D Charts
Tab chartsTab = new Tab("2D Charts", createChartsPane());
chartsTab.setClosable(false);
// Tab 2: 3D Graphics
Tab threeDTab = new Tab("3D Graphics", create3DPane());
threeDTab.setClosable(false);
tabPane.getTabs().addAll(chartsTab, threeDTab);
// Create scene
Scene scene = new Scene(tabPane, 1200, 800);
// Configure stage
primaryStage.setTitle("JavaFX Charts & 3D Graphics Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
private BorderPane createChartsPane() {
BorderPane pane = new BorderPane();
pane.setPadding(new Insets(20));
pane.setStyle("-fx-background-color: #f8f9fa;");
// Top: Controls
VBox controls = createChartControls();
pane.setTop(controls);
// Center: Charts grid
GridPane chartsGrid = new GridPane();
chartsGrid.setHgap(20);
chartsGrid.setVgap(20);
chartsGrid.setPadding(new Insets(20));
// Row 1: Line Chart & Bar Chart
VBox lineChartBox = createLineChart();
VBox barChartBox = createBarChart();
chartsGrid.add(lineChartBox, 0, 0);
chartsGrid.add(barChartBox, 1, 0);
// Row 2: Pie Chart & Area Chart
VBox pieChartBox = createPieChart();
VBox areaChartBox = createAreaChart();
chartsGrid.add(pieChartBox, 0, 1);
chartsGrid.add(areaChartBox, 1, 1);
pane.setCenter(chartsGrid);
return pane;
}
private VBox createChartControls() {
VBox controls = new VBox(15);
controls.setPadding(new Insets(10));
controls.setStyle("-fx-background-color: white; -fx-background-radius: 8px;");
Label title = new Label("Chart Controls");
title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #3D8B37;");
HBox buttonBox = new HBox(10);
Button refreshBtn = new Button("Refresh Data");
refreshBtn.setStyle("-fx-background-color: #3D8B37; -fx-text-fill: white;");
Button exportBtn = new Button("Export Chart");
exportBtn.setStyle("-fx-background-color: #00A8E8; -fx-text-fill: white;");
ComboBox themeCombo = new ComboBox<>();
themeCombo.getItems().addAll("Default", "Dark", "Colorful", "Monochrome");
themeCombo.setValue("Default");
themeCombo.setPromptText("Select Theme");
buttonBox.getChildren().addAll(refreshBtn, exportBtn, themeCombo);
controls.getChildren().addAll(title, buttonBox);
return controls;
}
private VBox createLineChart() {
VBox chartBox = new VBox(10);
chartBox.setPadding(new Insets(15));
chartBox.setStyle("-fx-background-color: white; -fx-background-radius: 8px;");
Label title = new Label("Monthly Sales Trend");
title.setStyle("-fx-font-weight: bold;");
// Define X and Y axis
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("Sales ($)");
// Create line chart
final LineChart lineChart = new LineChart<>(xAxis, yAxis);
lineChart.setTitle("2024 Sales Performance");
lineChart.setLegendVisible(true);
lineChart.setPrefSize(400, 300);
// Series 1: Product A
XYChart.Series series1 = new XYChart.Series<>();
series1.setName("Product A");
series1.getData().addAll(
new XYChart.Data<>("Jan", 23000),
new XYChart.Data<>("Feb", 28000),
new XYChart.Data<>("Mar", 32000),
new XYChart.Data<>("Apr", 31000),
new XYChart.Data<>("May", 35000),
new XYChart.Data<>("Jun", 42000)
);
// Series 2: Product B
XYChart.Series series2 = new XYChart.Series<>();
series2.setName("Product B");
series2.getData().addAll(
new XYChart.Data<>("Jan", 18000),
new XYChart.Data<>("Feb", 21000),
new XYChart.Data<>("Mar", 25000),
new XYChart.Data<>("Apr", 29000),
new XYChart.Data<>("May", 33000),
new XYChart.Data<>("Jun", 38000)
);
// Series 3: Product C
XYChart.Series series3 = new XYChart.Series<>();
series3.setName("Product C");
series3.getData().addAll(
new XYChart.Data<>("Jan", 12000),
new XYChart.Data<>("Feb", 15000),
new XYChart.Data<>("Mar", 18000),
new XYChart.Data<>("Apr", 22000),
new XYChart.Data<>("May", 26000),
new XYChart.Data<>("Jun", 30000)
);
lineChart.getData().addAll(series1, series2, series3);
// Style the chart
lineChart.lookup(".chart-plot-background").setStyle("-fx-background-color: transparent;");
chartBox.getChildren().addAll(title, lineChart);
return chartBox;
}
private VBox createBarChart() {
VBox chartBox = new VBox(10);
chartBox.setPadding(new Insets(15));
chartBox.setStyle("-fx-background-color: white; -fx-background-radius: 8px;");
Label title = new Label("Quarterly Revenue by Region");
title.setStyle("-fx-font-weight: bold;");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart barChart = new BarChart<>(xAxis, yAxis);
barChart.setTitle("Regional Performance");
barChart.setLegendVisible(true);
barChart.setPrefSize(400, 300);
// Q1 Data
XYChart.Series q1Series = new XYChart.Series<>();
q1Series.setName("Q1");
q1Series.getData().addAll(
new XYChart.Data<>("North", 45000),
new XYChart.Data<>("South", 38000),
new XYChart.Data<>("East", 42000),
new XYChart.Data<>("West", 39000)
);
// Q2 Data
XYChart.Series q2Series = new XYChart.Series<>();
q2Series.setName("Q2");
q2Series.getData().addAll(
new XYChart.Data<>("North", 52000),
new XYChart.Data<>("South", 45000),
new XYChart.Data<>("East", 48000),
new XYChart.Data<>("West", 46000)
);
// Q3 Data
XYChart.Series q3Series = new XYChart.Series<>();
q3Series.setName("Q3");
q3Series.getData().addAll(
new XYChart.Data<>("North", 58000),
new XYChart.Data<>("South", 51000),
new XYChart.Data<>("East", 55000),
new XYChart.Data<>("West", 53000)
);
barChart.getData().addAll(q1Series, q2Series, q3Series);
chartBox.getChildren().addAll(title, barChart);
return chartBox;
}
private VBox createPieChart() {
VBox chartBox = new VBox(10);
chartBox.setPadding(new Insets(15));
chartBox.setStyle("-fx-background-color: white; -fx-background-radius: 8px;");
Label title = new Label("Market Share Distribution");
title.setStyle("-fx-font-weight: bold;");
ObservableList pieChartData = FXCollections.observableArrayList(
new PieChart.Data("Company A", 35),
new PieChart.Data("Company B", 25),
new PieChart.Data("Company C", 20),
new PieChart.Data("Company D", 12),
new PieChart.Data("Others", 8)
);
final PieChart pieChart = new PieChart(pieChartData);
pieChart.setTitle("Market Share 2024");
pieChart.setLabelsVisible(true);
pieChart.setLegendVisible(true);
pieChart.setPrefSize(400, 300);
pieChart.setStartAngle(90); // Start at top
// Add hover effects
for (PieChart.Data data : pieChartData) {
data.getNode().setOnMouseEntered(e -> {
data.getNode().setScaleX(1.1);
data.getNode().setScaleY(1.1);
});
data.getNode().setOnMouseExited(e -> {
data.getNode().setScaleX(1.0);
data.getNode().setScaleY(1.0);
});
}
chartBox.getChildren().addAll(title, pieChart);
return chartBox;
}
private VBox createAreaChart() {
VBox chartBox = new VBox(10);
chartBox.setPadding(new Insets(15));
chartBox.setStyle("-fx-background-color: white; -fx-background-radius: 8px;");
Label title = new Label("Website Traffic Analysis");
title.setStyle("-fx-font-weight: bold;");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final AreaChart areaChart = new AreaChart<>(xAxis, yAxis);
areaChart.setTitle("Daily Visitors");
areaChart.setLegendVisible(true);
areaChart.setPrefSize(400, 300);
XYChart.Series desktopSeries = new XYChart.Series<>();
desktopSeries.setName("Desktop");
desktopSeries.getData().addAll(
new XYChart.Data<>("Mon", 4500),
new XYChart.Data<>("Tue", 5200),
new XYChart.Data<>("Wed", 4800),
new XYChart.Data<>("Thu", 5100),
new XYChart.Data<>("Fri", 4900),
new XYChart.Data<>("Sat", 3800),
new XYChart.Data<>("Sun", 3200)
);
XYChart.Series mobileSeries = new XYChart.Series<>();
mobileSeries.setName("Mobile");
mobileSeries.getData().addAll(
new XYChart.Data<>("Mon", 3800),
new XYChart.Data<>("Tue", 4200),
new XYChart.Data<>("Wed", 4100),
new XYChart.Data<>("Thu", 4500),
new XYChart.Data<>("Fri", 4300),
new XYChart.Data<>("Sat", 5200),
new XYChart.Data<>("Sun", 4800)
);
areaChart.getData().addAll(desktopSeries, mobileSeries);
chartBox.getChildren().addAll(title, areaChart);
return chartBox;
}
private BorderPane create3DPane() {
BorderPane pane = new BorderPane();
pane.setPadding(new Insets(20));
pane.setStyle("-fx-background-color: #1a2a3a;");
// Top: Controls
HBox controls = create3DControls();
pane.setTop(controls);
// Center: 3D Scene
SubScene subScene = create3DScene();
pane.setCenter(subScene);
return pane;
}
private HBox create3DControls() {
HBox controls = new HBox(15);
controls.setPadding(new Insets(10));
controls.setStyle("-fx-background-color: rgba(255, 255, 255, 0.1); -fx-background-radius: 8px;");
controls.setAlignment(Pos.CENTER_LEFT);
Label title = new Label("3D Scene Controls");
title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: white;");
Button rotateBtn = new Button("Auto Rotate");
rotateBtn.setStyle("-fx-background-color: #3D8B37; -fx-text-fill: white;");
Button resetBtn = new Button("Reset View");
resetBtn.setStyle("-fx-background-color: #00A8E8; -fx-text-fill: white;");
Label instruction = new Label("Drag to rotate • Scroll to zoom");
instruction.setStyle("-fx-text-fill: #aaa; -fx-font-style: italic;");
controls.getChildren().addAll(title, rotateBtn, resetBtn, instruction);
return controls;
}
private SubScene create3DScene() {
// Create 3D group
Group root3D = new Group();
// Create camera
camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-1000);
// Add transforms for mouse control
root3D.getTransforms().addAll(rotateX, rotateY);
// Create 3D objects
create3DObjects(root3D);
// Create subscene
SubScene subScene = new SubScene(root3D, 800, 600, true, SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
subScene.setFill(Color.rgb(30, 40, 50));
// Add mouse controls
setupMouseControls(subScene, root3D);
return subScene;
}
private void create3DObjects(Group root) {
// Create a 3D cube
Box cube = new Box(200, 200, 200);
cube.setTranslateX(-300);
cube.setMaterial(new PhongMaterial(Color.RED));
cube.setDrawMode(DrawMode.FILL);
// Create a sphere
Sphere sphere = new Sphere(120);
sphere.setTranslateX(0);
sphere.setMaterial(new PhongMaterial(Color.BLUE));
// Create a cylinder
Cylinder cylinder = new Cylinder(80, 200);
cylinder.setTranslateX(300);
cylinder.setMaterial(new PhongMaterial(Color.GREEN));
// Create a pyramid (custom mesh)
MeshView pyramid = createPyramid(150, 200);
pyramid.setTranslateX(-300);
pyramid.setTranslateY(300);
pyramid.setMaterial(new PhongMaterial(Color.ORANGE));
// Create a torus (donut)
MeshView torus = createTorus(100, 40);
torus.setTranslateX(0);
torus.setTranslateY(300);
torus.setMaterial(new PhongMaterial(Color.PURPLE));
// Create a cone
Cone cone = new Cone(100, 200);
cone.setTranslateX(300);
cone.setTranslateY(300);
cone.setMaterial(new PhongMaterial(Color.YELLOW));
// Add lighting
PointLight pointLight = new PointLight(Color.WHITE);
pointLight.setTranslateX(-500);
pointLight.setTranslateY(-500);
pointLight.setTranslateZ(-500);
AmbientLight ambientLight = new AmbientLight(Color.rgb(50, 50, 50));
// Add grid floor
Group grid = createGrid(1000, 1000, 50);
grid.setTranslateY(200);
// Add all objects to root
root.getChildren().addAll(cube, sphere, cylinder, pyramid, torus, cone, grid, pointLight, ambientLight);
}
private MeshView createPyramid(double size, double height) {
float[] points = {
// Base vertices
(float)-size, 0, (float)-size,
(float)size, 0, (float)-size,
(float)size, 0, (float)size,
(float)-size, 0, (float)size,
// Apex
0, (float)-height, 0
};
int[] faces = {
// Base
0, 0, 1, 0, 2, 0,
0, 0, 2, 0, 3, 0,
// Sides
0, 0, 4, 0, 1, 0,
1, 0, 4, 0, 2, 0,
2, 0, 4, 0, 3, 0,
3, 0, 4, 0, 0, 0
};
TriangleMesh mesh = new TriangleMesh();
mesh.getPoints().addAll(points);
mesh.getFaces().addAll(faces);
return new MeshView(mesh);
}
private MeshView createTorus(double radius, double tubeRadius) {
final int divR = 32;
final int divT = 24;
TriangleMesh mesh = new TriangleMesh();
// Create vertices
for (int i = 0; i <= divR; i++) {
double r = 2 * Math.PI * i / divR;
double cosR = Math.cos(r);
double sinR = Math.sin(r);
for (int j = 0; j <= divT; j++) {
double t = 2 * Math.PI * j / divT;
double cosT = Math.cos(t);
double sinT = Math.sin(t);
float x = (float)((radius + tubeRadius * cosT) * cosR);
float y = (float)((radius + tubeRadius * cosT) * sinR);
float z = (float)(tubeRadius * sinT);
mesh.getPoints().addAll(x, y, z);
}
}
// Create faces
for (int i = 0; i < divR; i++) {
for (int j = 0; j < divT; j++) {
int p0 = i * (divT + 1) + j;
int p1 = p0 + 1;
int p2 = p0 + (divT + 1);
int p3 = p2 + 1;
mesh.getFaces().addAll(p0, 0, p2, 0, p1, 0);
mesh.getFaces().addAll(p2, 0, p3, 0, p1, 0);
}
}
return new MeshView(mesh);
}
private Group createGrid(double size, double spacing, int divisions) {
Group grid = new Group();
// Create grid lines
for (int i = -divisions; i <= divisions; i++) {
double position = i * spacing;
// Horizontal lines
Line hLine = new Line(-size, position, size, position);
hLine.setStroke(Color.rgb(100, 100, 100, 0.3));
hLine.setStrokeWidth(1);
// Vertical lines
Line vLine = new Line(position, -size, position, size);
vLine.setStroke(Color.rgb(100, 100, 100, 0.3));
vLine.setStrokeWidth(1);
grid.getChildren().addAll(hLine, vLine);
}
// Center lines
Line centerX = new Line(-size, 0, size, 0);
centerX.setStroke(Color.rgb(200, 200, 200, 0.5));
centerX.setStrokeWidth(2);
Line centerY = new Line(0, -size, 0, size);
centerY.setStroke(Color.rgb(200, 200, 200, 0.5));
centerY.setStrokeWidth(2);
grid.getChildren().addAll(centerX, centerY);
return grid;
}
private void setupMouseControls(SubScene subScene, Group root3D) {
subScene.setOnMousePressed(event -> {
mousePosX = event.getSceneX();
mousePosY = event.getSceneY();
});
subScene.setOnMouseDragged(event -> {
double deltaX = event.getSceneX() - mousePosX;
double deltaY = event.getSceneY() - mousePosY;
rotateX.setAngle(rotateX.getAngle() - deltaY * 0.5);
rotateY.setAngle(rotateY.getAngle() + deltaX * 0.5);
mousePosX = event.getSceneX();
mousePosY = event.getSceneY();
});
subScene.setOnScroll(event -> {
double deltaZ = event.getDeltaY() * 10;
camera.setTranslateZ(camera.getTranslateZ() + deltaZ);
});
}
public static void main(String[] args) {
launch(args);
}
}
| Chart Type | Class | Best For | Features |
|---|---|---|---|
| Line Chart | LineChart |
Trends over time | Multiple series, data points, tooltips |
| Bar Chart | BarChart |
Comparing categories | Stacked bars, grouped bars, colors |
| Pie Chart | PieChart |
Proportions/percentages | Interactive slices, legends, tooltips |
| Area Chart | AreaChart |
Cumulative data | Stacked areas, gradient fills |
| Scatter Chart | ScatterChart |
Correlations | Data point clusters, regression lines |
| Bubble Chart | BubbleChart |
3D data visualization | Variable bubble sizes, colors |
7. Best Practices & Performance
- Blocking JavaFX Thread: Performing long operations on UI thread
- Memory Leaks: Not removing listeners and bindings
- Overusing Animations: Too many animations causing performance issues
- Poor CSS Organization: Inline styles instead of CSS files
- Ignoring FXML: Writing all UI in code instead of FXML
- Not Using Properties: Missing data binding opportunities
JavaFX Best Practices
- Always use FXML for UI layout
- Separate CSS styles from Java code
- Use Properties and Bindings for data synchronization
- Implement Model-View-Controller (MVC) pattern
- Use Task/Service for background operations
- Optimize Scene Graph (minimize nodes, use Canvas for complex drawings)
- Implement proper error handling and user feedback
Performance Tips
- Use Canvas: For complex, dynamic drawings instead of many nodes
- Cache nodes:
node.setCache(true)for static content - Lazy loading: Load complex UI parts on demand
- Virtualized controls: Use ListView/TableView with cell factories
- Batch updates: Use
Platform.runLater()for UI updates - Monitor memory: Use VisualVM or Java Mission Control
- Test on target hardware: Different GPUs perform differently
Key Takeaways
- JavaFX is the modern replacement for Swing
- FXML & Scene Builder enable separation of UI and logic
- CSS Styling provides professional-looking interfaces
- Properties & Bindings simplify data synchronization
- Animations & Effects create engaging user experiences
- Charts & 3D enable advanced data visualization
- Performance optimization is crucial for complex applications
| Feature | JavaFX | Swing | Modern Web |
|---|---|---|---|
| Rendering | Hardware Accelerated | CPU-based | GPU Accelerated |
| Styling | CSS | Look & Feel | CSS |
| Layout | FXML/Scene Builder | Code/Matisse | HTML/CSS |
| Performance | Excellent | Poor | Good (depends) |
| 3D Support | Built-in | None | WebGL |
| Deployment | Native/JNLP | JAR/Web Start | Web Server |