Zajecia 9. - JavaFX - cwiczenia demonstracyjne (hands-on)
Organizacja czesci praktycznej
- Czas: 20-30 min
- Forma: prowadzący koduje na zywo, studenci odtwarzaja kroki
- Cel: przejsc od prostego UI do formularza z FXML i CSS
Demo 1 (latwe): Kalkulator netto/brutto
Cel:
- Uzyc
TextField,ComboBox,Button,Label - Obsluzyc zdarzenie klikniecia i walidacje danych
Zadanie demonstracyjne:
- Pole kwoty netto
- Lista stawek VAT (23%, 8%, 5%)
- Przycisk
Przelicz - Wynik brutto w etykiecie
Wskazowki:
- Parsuj wejscie przez
Double.parseDouble - Przy bledzie wyswietl komunikat
- Sformatuj wynik do 2 miejsc po przecinku
Rozwiazanie:
import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.GridPane; import javafx.stage.Stage; public class DemoVatCalculator extends Application { @Override public void start(Stage stage) { TextField netField = new TextField(); ComboBox<Double> vatBox = new ComboBox<>(); vatBox.getItems().addAll(0.23, 0.08, 0.05); vatBox.setValue(0.23); Button calcBtn = new Button("Przelicz"); Label result = new Label("Brutto: -"); calcBtn.setOnAction(e -> { try { double net = Double.parseDouble(netField.getText().trim()); double gross = net * (1.0 + vatBox.getValue()); result.setText(String.format("Brutto: %.2f PLN", gross)); } catch (NumberFormatException ex) { result.setText("Blad: podaj poprawna liczbe"); } }); GridPane root = new GridPane(); root.setPadding(new Insets(12)); root.setHgap(8); root.setVgap(8); root.add(new Label("Netto:"), 0, 0); root.add(netField, 1, 0); root.add(new Label("VAT:"), 0, 1); root.add(vatBox, 1, 1); root.add(calcBtn, 1, 2); root.add(result, 1, 3); stage.setScene(new Scene(root, 420, 220)); stage.setTitle("Demo 1 - VAT"); stage.show(); } public static void main(String[] args) { launch(args); } }
Demo 2 (srednie): Lista zadan TODO + binding
Cel:
- Uzyc
ObservableList,ListView,TextField - Pokazac prosty binding aktywacji przycisku
Zadanie demonstracyjne:
- Pole tekstowe na nazwe zadania
- Przycisk
Dodaj(aktywny tylko, gdy pole niepuste) ListViewz zadaniami- Przycisk
Usun zaznaczone
Wskazowki:
disableProperty().bind(…)dla przyciskugetSelectionModel().getSelectedIndex()dla usuwania- Puste wejscie ignoruj przez
trim().isEmpty()
Rozwiazanie:
import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.stage.Stage; public class DemoTodoList extends Application { @Override public void start(Stage stage) { ObservableList<String> tasks = FXCollections.observableArrayList(); TextField input = new TextField(); input.setPromptText("Nowe zadanie"); Button addBtn = new Button("Dodaj"); Button removeBtn = new Button("Usun zaznaczone"); addBtn.disableProperty().bind( Bindings.createBooleanBinding( () -> input.getText().trim().isEmpty(), input.textProperty() ) ); ListView<String> listView = new ListView<>(tasks); addBtn.setOnAction(e -> { String value = input.getText().trim(); if (!value.isEmpty()) { tasks.add(value); input.clear(); } }); removeBtn.setOnAction(e -> { int idx = listView.getSelectionModel().getSelectedIndex(); if (idx >= 0) { tasks.remove(idx); } }); HBox top = new HBox(8, input, addBtn, removeBtn); BorderPane root = new BorderPane(); root.setTop(top); root.setCenter(listView); stage.setScene(new Scene(root, 560, 320)); stage.setTitle("Demo 2 - TODO"); stage.show(); } public static void main(String[] args) { launch(args); } }
Demo 3 (trudniejsze): FXML + CSS + kontroler
Cel:
- Pokazac workflow: FXML (widok) + Controller (logika) + CSS (styl)
Zadanie demonstracyjne:
- Widok logowania (email, haslo, przycisk)
- Walidacja prostych danych i komunikat statusu
- Stylowanie przycisku i tla
Wskazowki:
- Polacz
fx:idz polami@FXML onActionkieruj do metody w kontrolerze- Arkusz CSS podlacz przez
scene.getStylesheets().add(…)
Rozwiazanie - kontroler:
package demo; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.TextField; public class LoginController { @FXML private TextField emailField; @FXML private PasswordField passwordField; @FXML private Label statusLabel; @FXML private void onLogin() { String email = emailField.getText().trim(); String pass = passwordField.getText(); boolean validEmail = email.contains("@") && email.contains("."); boolean validPass = pass.length() >= 8; if (validEmail && validPass) { statusLabel.setText("Logowanie poprawne"); } else { statusLabel.setText("Sprawdz email i haslo (min. 8 znakow)"); } } }
Rozwiazanie - FXML:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.VBox?> <VBox xmlns:fx="http://javafx.com/fxml" fx:controller="demo.LoginController" spacing="10" styleClass="app-root"> <padding><Insets top="14" right="14" bottom="14" left="14"/></padding> <Label text="Panel logowania" styleClass="title"/> <TextField fx:id="emailField" promptText="Email"/> <PasswordField fx:id="passwordField" promptText="Haslo"/> <Button text="Zaloguj" onAction="#onLogin" id="primaryButton"/> <Label fx:id="statusLabel" text="Wpisz dane"/> </VBox>
Rozwiazanie - CSS:
.app-root { -fx-background-color: linear-gradient(to bottom, #f8fafc, #e5ecf4); } .title { -fx-font-size: 18px; -fx-font-weight: bold; } #primaryButton { -fx-background-radius: 8; -fx-font-weight: bold; }
Rozwiazanie - uruchomienie:
FXMLLoader loader = new FXMLLoader(getClass().getResource("login-view.fxml")); Scene scene = new Scene(loader.load(), 420, 260); scene.getStylesheets().add(getClass().getResource("app.css").toExternalForm()); stage.setScene(scene); stage.show();
Podsumowanie hands-on
- Demo 1: kontrolki + eventy + walidacja
- Demo 2: listy danych + binding
- Demo 3: FXML + kontroler + CSS
- Efekt: pelny fundament pod mini-projekty JavaFX