====== 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 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)
* ''ListView'' z zadaniami
* Przycisk ''Usun zaznaczone''
Wskazowki:
* ''disableProperty().bind(...)'' dla przycisku
* ''getSelectionModel().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 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 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:id'' z polami ''@FXML''
* ''onAction'' kieruj 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:
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