[[oktatas:programozás:java:java_fx|< Java FX]]
====== Scene Builder ======
* **Szerző:** Sallai András
* Copyright (c) 2021, Sallai András
* Szerkesztve: 2021, 2022, 2024
* Licenc: [[https://creativecommons.org/licenses/by-sa/4.0/|CC Attribution-Share Alike 4.0 International]]
* Web: https://szit.hu
===== A SceneBuilder beszerzése =====
* https://gluonhq.com/products/scene-builder/ (2022)
Ez csak a régi Java 8-hoz van:
* https://www.oracle.com/java/technologies/javase/javafxscenebuilder-info.html
Rendelkezésre áll:
* Windowsos .msi csomag. Például: SceneBuilder-19.0.0.msi
* Linuxos .deb csomag. Például: SceneBuilder-19.0.0.deb
==== Windowson ====
Windowson az .msi fájl alapértelmezetten profilba telepszik.
==== Linuxon ====
A linuxos verzió telepítése:
dpkg -i SceneBuilder-19.0.0.deb
Az /opt könyvtárba telepszik.
Debian GNU/Linux 12-ön, telepítés előtt létre kell hozni egy desktop-directories könyvtárat:
sudo mkdir /usr/share/desktop-directories/
Nélküle a következő hibát kapjuk:
xdg-desktop-menu: No writable system menu directory found.
===== Visual Studio Code =====
==== Bővítmény ====
Bővítmény telepítése:
* SceneBuilder extension for Visual Studio Code
A parancs panelon keresztül konfiguráljuk. Gépeljük be:
Scene
Válasszuk a következőt:
Configure Scene Builder path
Keressük meg az előugró párbeszédablakban a futtatható SceneBuilder szerkesztőt.
Például:
/opt/scenebuilder
Amit ki kell választani:
/opt/scenebuilder/bin
Windows esetén, egy lehetséges útvonal:
c:\Users\janos\AppData\Local\SceneBuilder\
===== VSCodium =====
==== Bővítmény ====
A következő bővítményre van szükség:
* SceneBuilder extension for Visual Studio Code
A bővítmény nem érhető el tárolóból. Ezért töltsük le innen:
* https://marketplace.visualstudio.com/items?itemName=bilalekrem.scenebuilderextension (2024)
Jobb oldalon találunk egy "Download Extension" linket.
Letöltés után, ha 1.0.1 verzónk van:
codium --install-extension bilalekrem.scenebuilderextension-1.0.1.vsix
===== VSCode és VSCodium beállítása =====
A parancs panelon keresztül konfiguráljuk. Gépeljük be:
Scene
Válasszuk a következőt:
Configure Scene Builder path
Keressük meg az előugró párbeszédablakban a futtatható SceneBuilder szerkesztőt.
Amit ki kell választani:
/opt/scenebuilder/bin
===== VSCode és VSCodium használat =====
Készítsünk egy üres állományt, például:
mainScene.fxml
vagy:
app.fxml
Az állományon **jobb egér gomb**, majd:
* Open in Scene Builder
{{:oktatas:programozas:java:java_fx:vscode_open_scenebuilder.png?500|}}
A SceneBuilder 21 esetén ha **main.fxml** nevet adok a fájlnak,
akkor megnyitáskor eltűnik az ablak keret minden vezérlővel.
===== Az FXML betöltése =====
A következő sor szükséges az fxml fájl használatához.
Parent root = FXMLLoader.load(getClass().getResource("mainScene.fxml"));
Ezek után a Scene() konstruktorában megadjuk:
Scene scene1 = new Scene(root, 300, 250);
A teljes start() függvény:
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("mainScene.fxml"));
Scene scene1 = new Scene(root, 300, 250);
primaryStage.setScene(scene1);
primaryStage.show();
}
A kivételeket eldobtuk. A getResource() az IOException kivételt vált ki.
===== Eseménykezelés =====
Eseménykezeléshez meg kell adni:
* A kezelő metódus nevét a jobboldalon a Button "Code" tulajdonságainál.
* Bal oldalon a "Controller" tulajdonságoknál meg kell adni a kontroller osztályt.
* Ha még nincs kontroller megadható, például az App osztály.
{{:oktatas:programozas:java:java_fx:javafx_scenebuilder.png?500|}}
Ha a MainController könyvtárban van, például egy controllers könyvtárban:
controllers.MainController
Vegyük észre a (.) pont karaktert, a (/) perjel vagy a (\) visszaperjel karakter helyett.
Eseménykezelő az App.java fájlon belül:
@FXML
protected void onClickMehetGomb(ActionEvent event) {
System.out.println("Működik");
}
Ha akcióknak van egy célkomponense azt szerepeltetni kell a SceneBuilder
Code részén, a fx:id mezőben, egy tetszőleges névvel:
Ugyanennek a névnek szerepelnie kell az osztályban, ahol hivatkozni szeretnénk rá,
egy FXML annotációval:
@FXML private Text celfelirat;
Együtt az eseménykezelővel:
@FXML
private Label celfelirat;
@FXML
protected void onClickMehetGomb(ActionEvent event) {
celfelirat.setText("Működik");
}
===== Teljes kód =====
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
public class App extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("app.fxml"));
Scene scene1 = new Scene(root, 300, 250);
primaryStage.setScene(scene1);
primaryStage.show();
}
@FXML
private Label celfelirat;
@FXML
protected void onClickMehetGomb(ActionEvent event) {
celfelirat.setText("Működik");
}
}
===== MVC =====
Adott egy projekt, ahol a kontroller külön osztályban van.
A következő fájl és könyvtárszerkezet van:
app01/
|-lib/
|-src/
| |-controllers/
| | `-MainController.java
| |-models/
| | `-MainModel.java
| |-views/
| | `-MainScene.fxml
| `-App.java
`-README.md
A kontroller a controllers/MainController.java fájlban van.
A nézet a mainScene.fxml fájlban.
Mivel írni akarjuk a TextField mezőt, adnunk kell egy fx:id azonosítót:
{{:oktatas:programozas:java:java_fx:javafx_scenebuilder_textfiled1.png?500|}}
Megmondjuk a kontroller osztály nevét:
{{:oktatas:programozas:java:java_fx:javafx_scenebuilder_button.png?500|}}
package controllers;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import models.MainModel;
public class MainController {
MainModel mainModel;
@FXML
private TextField textfield1;
public MainController() {
this.mainModel = new MainModel();
}
@FXML
protected void onClickMehetGomb(ActionEvent event) {
System.out.println("Működik");
Integer num = this.mainModel.getNum();
textfield1.setText(num.toString());
}
}
package models;
public class MainModel {
public int getNum() {
return 15;
}
}
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class App extends Application {
public static void main(String[] args) throws Exception {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("views/MainScene.fxml"));
Scene scene1 = new Scene(root, 300, 250);
primaryStage.setScene(scene1);
primaryStage.show();
}
}
===== Példa GitHubon =====
* https://github.com/oktat/extriangle_javafx.git
===== VSCode beépített projektkészítő =====
* F1
* Java: Create Java Project...
* JavaFX - (create from archetype) (Provided by Maven for Java)
* group Id: com.example
* artifact Id: demo
* Kiválasztjuk milyen könyvtárban hozzuk létre a projektet [Kiválasztás]
A Maven bekéri a verziót:
* 'version' 1.0-SNAPSHOT: :
* javafx-version: 13 Y: :
* Enter
A VSCode feldob egy ablakot, van rajta egy [Open] gomb. A gombra kattintva a projekt megnyílik.
==== Kontrollerek könyvtára ====
module com.example {
requires javafx.controls;
requires javafx.fxml;
opens com.example to javafx.fxml;
exports com.example;
}
Hozzuk létre egy controllers könyvtárat, abban például egy MainController.java fájlt:
package com.example.controllers;
public class MainController {
}
Ha szeretnénk létrehozni egy controllers könyvtárat, amit a FXML-ben (SceneBuilder-ben) szeretnénk használni fel kell vennünk ebbe a fájlba:
module com.example {
requires javafx.controls;
requires javafx.fxml;
opens com.example to javafx.fxml;
opens com.example.controllers to javafx.fxml;
exports com.example;
}
Így beállítható a kontroller a SceneBuilder felületén:
com.example.controllers.MainController
===== Táblázat =====
A modell osztályban kötelező a getter, setter metódusok létrehozása.
Implementálni kell az Initializable interfészt:
public class UserController implements Initializable {
}
Az egyes osztlopok beállítása:
@Override
public void initialize( URL url, ResourceBundle rb ) {
this.idcol.setCellValueFactory(new PropertyValueFactory<>("id"));
this.usernamecol.setCellValueFactory(new PropertyValueFactory<>("username"));
this.passwordcol.setCellValueFactory(new PropertyValueFactory<>("password"));
this.admincol.setCellValueFactory(new PropertyValueFactory<>("admin"));
this.enabledcol.setCellValueFactory(new PropertyValueFactory<>("enabled"));
}
Teljeskód egy UserController.java fájlban:
package lan.zold;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.CheckBox;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
public class UserController implements Initializable {
@FXML
private TextField username;
@FXML
private PasswordField password;
@FXML
private CheckBox admin;
@FXML
private CheckBox enabled;
@FXML
private TableView usertable;
@FXML
private TableColumn idcol;
@FXML
private TableColumn usernamecol;
@FXML
private TableColumn passwordcol;
@FXML
private TableColumn admincol;
@FXML
private TableColumn enabledcol;
@Override
public void initialize( URL url, ResourceBundle rb ) {
this.idcol.setCellValueFactory(new PropertyValueFactory<>("id"));
this.usernamecol.setCellValueFactory(new PropertyValueFactory<>("username"));
this.passwordcol.setCellValueFactory(new PropertyValueFactory<>("password"));
this.admincol.setCellValueFactory(new PropertyValueFactory<>("admin"));
this.enabledcol.setCellValueFactory(new PropertyValueFactory<>("enabled"));
}
@FXML
private void addUser() {
if(this.username.getText().isEmpty() ||
this.password.getText().isEmpty()) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Hiba!");
alert.setHeaderText("Hiba!");
alert.setContentText("A felhasználónév vagy a jelszó üres!");
alert.showAndWait();
return;
}
System.out.println("Add user");
String username = this.username.getText();
String password = this.password.getText();
boolean admin = this.admin.isSelected();
boolean enabled = this.enabled.isSelected();
System.out.println("User added: " +
username + " " + password + " " +
admin + " " + enabled);
Employee employee = new Employee();
employee.id = 0;
employee.username = username;
employee.password = password;
employee.admin = admin;
employee.enabled = enabled;
usertable.getItems().add(employee);
this.username.clear();
this.password.clear();
this.admin.setSelected(false);
this.enabled.setSelected(false);
}
@FXML
private void deleteUser() {
if(usertable.getSelectionModel().getSelectedItem() == null) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Hiba!");
alert.setHeaderText("Hiba!");
alert.setContentText("Nincs kijelölt elem!");
alert.showAndWait();
return;
}
System.out.println("Delete user");
usertable.getItems().remove(usertable.getSelectionModel().getSelectedItem());
}
@FXML
private void updateUser() {
if(usertable.getSelectionModel().getSelectedItem() == null) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle("Hiba!");
alert.setHeaderText("Felhasználó módosítása");
alert.setContentText("Nincs kijelölt elem!");
alert.showAndWait();
return;
}
System.out.println("Update user");
Employee employee = usertable.getSelectionModel().getSelectedItem();
employee.username = this.username.getText();
employee.password = this.password.getText();
employee.admin = this.admin.isSelected();
employee.enabled = this.enabled.isSelected();
usertable.refresh();
}
@FXML
private void closeUserStage() throws IOException {
App.setRoot("main");
}
}
==== ObservableList ====
@FXML
private TableView empTable;
//...
ArrayList emps = employeeSource.getEmployees();
ObservableList observableEmps = FXCollections.observableArrayList(emps);
empTable.setItems(observableEmps);
==== Gomb szelekcióhoz kötése ====
startButton.disableProperty()
.bind(table.getSelectionModel()
.selectedItemProperty().isNull());
package com.example;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
public class MainController {
@FXML
private TableView table;
@FXML
private TableColumn idCol;
@FXML
private TableColumn naemCol;
@FXML
private Button startButton;
@FXML
private void initialize() {
idCol.setCellValueFactory(new PropertyValueFactory<>("id"));
naemCol.setCellValueFactory(new PropertyValueFactory<>("name"));
ObservableList data = FXCollections.observableArrayList();
data.add(new Employee(1, "name1"));
data.add(new Employee(2, "name2"));
data.add(new Employee(3, "name3"));
data.add(new Employee(4, "name4"));
table.setItems(data);
startButton.disableProperty()
.bind(table.getSelectionModel()
.selectedItemProperty().isNull());
}
@FXML
void onClickStartButton(ActionEvent event) {
}
}
==== Gomb mező értéke alapján ====
Nem csak kiválasztottnak kell lennie, de a kiválasztott sor értéke
ne legyen "name3":
startButton.disableProperty()
.bind(table.getSelectionModel()
.selectedItemProperty().isNull()
.or(
Bindings.select(
table.getSelectionModel()
.selectedItemProperty(), "name")
.isEqualTo("name3")
)
);