Примерна задача
Да се създаде JavaFX приложение „Форма за регистрация“, което съдържа:
- поле за потребителско име (TextField);
- поле за парола (PasswordField);
- бутон „Регистрация“ (Button);
- текстово поле за статус (Label).
Приложението трябва:
- да валидира данните в реално време;
- да активира бутона само при валидни данни.
| 1. Реализация с imperative модел | 2. Реализация с Reactive модел |
| 1.1 Структура на приложението | |
ps-project/
└─ src/
└─ main/
├─ java/
│ └─ bg/
│ └─ tu_varna/
│ └─ sit/
│ └─ ps/
│ └─ lab4/
│ └─ task1/
│ ├─ RegistrationApplication.java
│ ├─ Launcher.java
│ └─ controller/
│ └─ RegistrationController.java
└─ resources/
└─ bg/
└─ tu_varna/
└─ sit/
└─ ps/
└─ lab4/
└─ task1/
└─ registration-view.fxml
|
ps-project/
└─ src/
└─ main/
├─ java/
│ └─ bg/
│ └─ tu_varna/
│ └─ sit/
│ └─ ps/
│ └─ lab4/
│ └─ task2/
│ ├─ RegistrationApplication.java
│ ├─ Launcher.java
│ └─ controller/
│ └─ RegistrationController.java
└─ resources/
└─ bg/
└─ tu_varna/
└─ sit/
└─ ps/
└─ lab4/
└─ task2/
└─ registration-view.fxml
|
| 3. Controller |
При imperative подхода: - логиката се изпълнява само при събитие;
- валидирането е ръчно;
- няма автоматична реакция при промяна на данни.
| При Reactive подхода: - използва JavaFX Properties;
- реагира в реално време;
- автоматично управлява UI чрез binding.
|
package controller;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class RegistrationController {
@FXML
private TextField usernameField;
@FXML
private PasswordField passwordField;
@FXML
private Button registerButton;
@FXML
private Label statusLabel;
@FXML
public void initialize() {
registerButton.setOnAction(e -> {
String username = usernameField.getText();
String password = passwordField.getText();
if (username.length() >= 4 && password.length() >= 6) {
statusLabel.setText("Данните са валидни");
} else {
statusLabel.setText("Невалидни данни");
}
});
}
}
| package controller;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.event.ActionEvent;
public class RegistrationController {
@FXML
private TextField usernameField;
@FXML
private PasswordField passwordField;
@FXML
private Button registerButton;
@FXML
private Label statusLabel;
// JavaFX Properties (reactive state)
private final BooleanProperty validUsername =
new SimpleBooleanProperty(false);
private final BooleanProperty validPassword =
new SimpleBooleanProperty(false);
@FXML
public void initialize() {
/* ===== Observer (ChangeListener + addListener) ===== */
usernameField.textProperty().addListener(
(obs, oldVal, newVal) ->
validUsername.set(newVal.length() >= 4)
);
passwordField.textProperty().addListener(
(obs, oldVal, newVal) ->
validPassword.set(newVal.length() >= 6)
);
/* ===== Binding ===== */
BooleanBinding formInvalid =
validUsername.not().or(validPassword.not());
registerButton.disableProperty().bind(formInvalid);
/* ===== Observer върху Binding ===== */
formInvalid.addListener((obs, oldVal, newVal) -> {
if (newVal) {
statusLabel.setText("Невалидни данни");
} else {
statusLabel.setText("Данните са валидни");
}
});
/* ===== EventHandler ===== */
registerButton.setOnAction(this::handleRegister);
/* ===== addEventHandler ===== */
registerButton.addEventHandler(
ActionEvent.ACTION,
e -> System.out.println("ActionEvent обработен")
);
}
private void handleRegister(ActionEvent event) {
statusLabel.setText("Регистрацията е успешна!");
}
}
|
- Валидирането става само при натискане на бутона
- UI не реагира при писане
- Липсва автоматична синхронизация
- Подходът е типичен за imperative модел
| - Валидирането става в реално време
- UI реагира при всяка промяна
- Има автоматична синхронизация чрез binding
- Подходът е типичен за reactive модел
|
| 4. FXML файл – потребителски интерфейс – registration-view.fxml |
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="10" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml"
fx:controller="bg.tu_varna.sit.ps.lab4.task1.controller.RegistrationController">
<TextField fx:id="usernameField"
promptText="Username"/>
<PasswordField fx:id="passwordField"
promptText="Password"/>
<Button fx:id="registerButton"
text="Регистрация"/>
<Label fx:id="statusLabel"
text="Въведете данни"/>
</VBox>
| <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.VBox?>
<VBox spacing="10" alignment="CENTER"
xmlns:fx="http://javafx.com/fxml"
fx:controller="bg.tu_varna.sit.ps.lab4.task2.controller.RegistrationController">
<TextField fx:id="usernameField"
promptText="Username"/>
<PasswordField fx:id="passwordField"
promptText="Password"/>
<Button fx:id="registerButton"
text="Регистрация"/>
<Label fx:id="statusLabel"
text="Въведете данни"/>
</VBox>
|
- FXML описва само UI, без логика
- fx:id осигурява връзка с controller-a
|
| 5. Application клас |
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class RegistrationApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
RegistrationApplication.class
.getResource("registration-view.fxml")
);
Parent root = loader.load();
Scene scene = new Scene(root, 300, 200);
stage.setTitle("JavaFX FXML imperative Example");
stage.setScene(scene);
stage.show();
}
}
| import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class RegistrationApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(
RegistrationApplication.class
.getResource("registration-view.fxml")
);
Parent root = loader.load();
Scene scene = new Scene(root, 300, 200);
stage.setTitle("JavaFX FXML Reactive Example");
stage.setScene(scene);
stage.show();
}
}
|
| 6. Main / Launcher клас |
import javafx.application.Application;
public class Launcher {
public static void main(String[] args) {
Application.launch(RegistrationApplication.class, args);
}
}
|
6. Сравнение на FXML imperative vs reactive
| Критерий | Imperative | Reactive |
| Реакция при въвеждане | Не | Да |
| Properties | Не | Да |
| Binding | Не | Да |
| Listener-и | Ограничени | Активни |
| Поддръжка | По-трудна | По-лесна |