Especialização em Sistemas com Java 21 LTS • Módulo de Banco de Dados e Projetos Integradores


🗺️ Mapa Conceitual do Tópico

flowchart TD
    subgraph LIFECYCLE["Ciclo de Vida JavaFX (Application)"]
        A["main(): Application.launch()"] --> B["init() (Background Thread)"]
        B --> C["start(Stage primary) (JavaFX App Thread)"]
        C --> D["stop() (Cleanup na finalização)"]
    end

    subgraph SCENE_GRAPH["Grafo de Cena (Scene Graph)"]
        E["Stage (Janela Nativa do SO)"]
        F["Scene (Contêiner de Dimensões e CSS)"]
        G["Parent / Root Layout (BorderPane / VBox / GridPane)"]
        H["Nós Folha (Button, TextField, TableView, Label)"]
        E --> F --> G --> H
    end

    subgraph MVC_BINDINGS["Padrão MVC & Reatividade"]
        I["View: Interface Declarativa FXML + CSS"]
        J["Controller: @FXML Injeção e Event Handling"]
        K["Model: Observable Properties (StringProperty, ObjectProperty)"]
        L["Data Binding Reativo (Unidirecional e Bidirecional)"]
        I <--> J <--> K
        K --> L
    end

    C --> SCENE_GRAPH
    SCENE_GRAPH --> MVC_BINDINGS

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style E fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style I fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style L fill:#c8e6c9,stroke:#388e3c,stroke-width:2px

🖥️ 1. A Arquitetura do JavaFX e o Grafo de Cena (Scene Graph)

O JavaFX é a plataforma canônica e moderna para o desenvolvimento de aplicações ricas para desktop (Rich Client Platforms - RCP) na JVM, sucedendo as bibliotecas históricas AWT e Swing.

Diferenciais Arquiteturais Chave:

  • Aceleração Gráfica por Hardware (Pipeline Prism): Todo o desenho de componentes visuais é renderizado pela GPU através de DirectX (Windows) ou Metal/OpenGL (Linux/macOS).
  • O Grafo de Cena (Scene Graph): Estrutura de dados em árvore hierárquica onde cada elemento gráfico (painéis, botões, formas 3D) é um nó (javafx.scene.Node).
  • JavaFX Application Thread: A thread dedicada ao processamento de eventos visuais da UI. Regra de Ouro: Qualquer atualização em componentes gráficos deve ocorrer estritamente nessa thread; operações assíncronas em segundo plano devem despachar atualizações visuais via Platform.runLater(() -> { ... }).

🎨 2. A Tríade MVC: FXML, Controladores e Estilização CSS

Para garantir desacoplamento absoluto entre engenharia de software e design de interfaces:

1. View Declarativa com FXML

O FXML é uma linguagem declarativa baseada em XML que define a hierarquia de nós da tela. Pode ser editado visualmente via Scene Builder:

<VBox spacing="10" alignment="CENTER" xmlns:fx="http://javafx.com/fxml" fx:controller="com.empresa.ui.PDVController">
    <Label text="Sistema de Ponto de Venda" styleClass="titulo-principal"/>
    <TextField fx:id="txtCodigoBarras" promptText="Digite o código SKU..."/>
    <Button text="Registrar Item" onAction="#onAdicionarItemAction"/>
</VBox>

2. O Controlador (@FXML Controller)

Classe Java responsável por interceptar as interações do usuário. A anotação @FXML permite que a engine de injeção conecte nós do arquivo XML com variáveis da classe sem chamadas manuais como findViewById:

public class PDVController implements Initializable {
    @FXML private TextField txtCodigoBarras;
    @FXML private TableView<ItemVenda> tabelaItens;

    @FXML
    private void onAdicionarItemAction() {
        // Lógica de manipulação
    }
}

3. Estilização com CSS NATIVO

JavaFX permite aplicar folhas de estilo CSS (style.css), utilizando propriedades com prefixo -fx- (-fx-background-color: #2c3e50;, -fx-border-radius: 8px;).


⚡ 3. Propriedades Reativas e Data Binding

Um dos recursos mais poderosos do JavaFX é o sistema de Observables e Properties (SimpleStringProperty, SimpleDoubleProperty, SimpleObjectProperty).

Sincronização Automática com Binding:

Elimina o código repetitivo de Listeners:

// O botão "Finalizar Venda" é desabilitado reativamente enquanto o carrinho estiver vazio
btnFinalizar.disableProperty().bind(carrinho.itensProperty().emptyProperty());

// Conecta o valor de um Slider a um Label de porcentagem bidirecionalmente
lblDesconto.textProperty().bind(sliderDesconto.valueProperty().asString("%.0f %%"));

📊 4. Matriz Comparativa: Plataformas de UI Desktop

Critério JavaFX Moderno Java Swing (Legado) Electron (Node + Chromium)
Aceleração Gráfica GPU Nativa (Prism) Renderização por CPU (Software) GPU via Chromium
Consumo de Memória RAM Baixo a Médio (~80 a 150 MB) Mínimo (~40 a 80 MB) Muito Alto (+400 MB)
Design e Temas CSS Moderno + FXML Pluggable Look and Feel rígido HTML5 / CSS3 / Tailwind
Bindings Reativos Nativos (Property API) Manuais (PropertyChangeListener) Reatividade via React/Vue
Distribuição Standalone jlink / jpackage (Instalador .exe nativo) JAR executável (exige JRE instalada) Empacotamento Node.js

💻 5. Código de Demonstração Corporativo (Java 21 LTS)

package com.empresa.ui;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
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.stage.Stage;

import java.math.BigDecimal;

/**
 * Ponto de Venda (PDV) corporativo reativo desenvolvido em JavaFX 21 puro.
 */
public class SimuladorPdvJavaFx extends Application {

    public record ProdutoPdv(String sku, String descricao, double preco) {}

    private final ObservableList<ProdutoPdv> itensVenda = FXCollections.observableArrayList();
    private final DoubleProperty totalVendaProperty = new SimpleDoubleProperty(0.0);

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Ponto de Venda Corporativo (JavaFX 21 LTS)");

        // Componentes Visuais
        Label lblTitulo = new Label("🛒 Caixa Aberto - Terminal PDV #01");
        lblTitulo.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: #1a237e;");

        TableView<ProdutoPdv> tabela = new TableView<>(itensVenda);
        TableColumn<ProdutoPdv, String> colSku = new TableColumn<>("SKU");
        colSku.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().sku()));

        TableColumn<ProdutoPdv, String> colDescricao = new TableColumn<>("Descrição do Produto");
        colDescricao.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(data.getValue().descricao()));
        colDescricao.setPrefWidth(250);

        TableColumn<ProdutoPdv, String> colPreco = new TableColumn<>("Preço (R$)");
        colPreco.setCellValueFactory(data -> new javafx.beans.property.SimpleStringProperty(String.format("R$ %.2f", data.getValue().preco())));

        tabela.getColumns().addAll(colSku, colDescricao, colPreco);

        // Formulário de Entrada
        TextField txtSku = new TextField();
        txtSku.setPromptText("SKU do item");
        TextField txtNome = new TextField();
        txtNome.setPromptText("Descrição");
        TextField txtValor = new TextField();
        txtValor.setPromptText("Preço unitário");

        Button btnAdicionar = new Button("➕ Incluir Produto");
        btnAdicionar.setStyle("-fx-background-color: #2e7d32; -fx-text-fill: white; -fx-font-weight: bold;");

        btnAdicionar.setOnAction(e -> {
            try {
                String sku = txtSku.getText().trim();
                String nome = txtNome.getText().trim();
                double valor = Double.parseDouble(txtValor.getText().replace(',', '.'));

                if (!sku.isEmpty() && !nome.isEmpty()) {
                    itensVenda.add(new ProdutoPdv(sku, nome, valor));
                    totalVendaProperty.set(totalVendaProperty.get() + valor);

                    txtSku.clear();
                    txtNome.clear();
                    txtValor.clear();
                    txtSku.requestFocus();
                }
            } catch (NumberFormatException ex) {
                Alert alert = new Alert(Alert.AlertType.ERROR, "Preço numérico inválido!", ButtonType.OK);
                alert.showAndWait();
            }
        });

        // Painel Inferior de Totais com Data Binding
        Label lblTotal = new Label();
        lblTotal.setStyle("-fx-font-size: 20px; -fx-font-weight: bold; -fx-text-fill: #b71c1c;");
        lblTotal.textProperty().bind(totalVendaProperty.asString("TOTAL A PAGAR: R$ %,.2f"));

        Button btnFinalizar = new Button("💳 Finalizar Venda");
        btnFinalizar.setStyle("-fx-background-color: #1565c0; -fx-text-fill: white; -fx-font-size: 14px;");
        btnFinalizar.disableProperty().bind(itensVenda.sizeProperty().isEqualTo(0)); // Desabilita se carrinho vazio

        btnFinalizar.setOnAction(e -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION, "Venda liquidada com sucesso no valor de " + lblTotal.getText(), ButtonType.OK);
            alert.showAndWait();
            itensVenda.clear();
            totalVendaProperty.set(0.0);
        });

        // Montagem do Layout
        HBox formBox = new HBox(10, txtSku, txtNome, txtValor, btnAdicionar);
        formBox.setAlignment(Pos.CENTER_LEFT);

        HBox footerBox = new HBox(20, lblTotal, btnFinalizar);
        footerBox.setAlignment(Pos.CENTER_RIGHT);

        VBox root = new VBox(15, lblTitulo, formBox, tabela, footerBox);
        root.setPadding(new Insets(15));

        Scene scene = new Scene(root, 750, 480);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        // Inicialização do runtime gráfico JavaFX
        launch(args);
    }
}