Ez a dokumentum egy előző változata!
Tartalomjegyzék
JavaFX - Eseménykezelés
- Szerző: Sallai András
- Copyright © 2025, Sallai András
- Web: https://szit.hu
Bevezetés
Ebben a leírásban VSCode JavaFX készítő bővítményét használtuk. A projekt amit kapunk FXML és Java kontroller fájlokkal dolgozik. Az alkalmazás belépési pontja pedig az App.java fájl.
Gomb
Az eseménykezelés megvalósítható az FXML fájlban meghatározott fx:id alapján is. Tegyük fel, hogy van egy button nevű nyomógombunk. Az initialize() metódusban a setOnAction() metódussal adhatom meg mit tegyünk kattintásra.
<Button fx:id="button" layoutX="270.0" layoutY="187.0" mnemonicParsing="false" text="Button" />
A következő példában a setOnAction() számára egy névtelen függvényt adunk meg, ami egyébként egy lambda kifejezés :
- MainController.java
@FXML private Button button; @FXML void initialize() { button.setOnAction(e -> startButton()); } void startButton() { System.out.println("start button"); }
Használhatunk metódusreferenciát is:
- MainController.java
@FXML private Button button; @FXML void initialize() { button.setOnAction(this::startButton); } void startButton(ActionEvent event) { System.out.println("start button"); }
A this::startButton a metódusreferencia, amivel megmondjuk melyik metódust kell végrehajtani kattintásra.
A két megvalósítás valójában ennek a rövidített változata:
@FXML private Button button; @FXML void initialize() { button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { startButton(); } }); } void startButton() { System.out.println("start button"); }
Az utóbbihoz tartozó teljeskód:
- MainController.java
package com.example; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.control.Button; public class MainController { @FXML private Button button; @FXML void initialize() { button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { startButton(); } }); } void startButton() { System.out.println("start button"); } }
Külön osztályt is létrehozhatunk ami implementálja a EventHandler<ActionEvent> interfészt. A példánkban egy beépített osztályt használunk. Teljes kód:
- MainController.java
package com.example; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.control.Button; public class MainController { @FXML private Button button; @FXML void initialize() { button.setOnAction(new ButtonHandler()); } void startButton() { System.out.println("start button"); } class ButtonHandler implements EventHandler<ActionEvent> { @Override public void handle(ActionEvent event) { startButton(); } } }
Eseménykezelő megadása
Az FXML fájlban megadhatjuk azt a metódust, amivel kezeljük az eseményt.
<Button layoutX="270.0" layoutY="187.0" mnemonicParsing="false" onAction="#onClickStartButton" text="Button" />
A Java kód ebben az esetben:
- MainController.java
package com.example; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Button; public class MainController { @FXML private Button button; @FXML void onClickStartButton(ActionEvent event) { startButton(); } void startButton() { System.out.println("start button"); } }
Alkalmazás indítása
Az alkalmazás indításakor ha szeretnénk a betöltött kontrollerekkel dolgozni az initialize() metódusra van szükségünk a kontrollerben. A metódust jelöljük meg az FXML annotációval.
Egy kontrollerben:
@FXML void initialize() { System.out.println("start"); }
Tötlsük fel például egy listView-t:
@FXML void initialize() { listView.getItems().addAll( "item1", "item2", "item3" ); }
A kontroller konstruktora nem használható, mivel annak lefutása idején még nem léteznek UI elemek.
Alkalmazás bezárása
Ha az alkalmazás bezárására szeretnénk reagálni, akkor az App.java fájlban írjuk felül a stop() metódust.
@Override public void stop() throws Exception { System.out.println("stop"); }
Lista mentése bezáráskor
Tegyük fel, hogy a VSCode JavaFX generáló bővítményét használtuk, ezért kaptunk egy App.java fájlt, és használunk valamilyen kontrollert, például MainController. Az alkalmazás tartalmaz egy listView objektumot, aminek az elemeit szeretnénk fájlba menteni.
Mivel a bezárás eseménynél szükségünk lesz a kontrollerre, ezért vegyünk fel egy statikus mainController objektumot, ami a start() metódusban előkészítünk. Ehhez szükség van az fxmlLoadedr objektumra, ami alapértelmezetten csak a loadFXML() metódusból érhető el. Ezért a fxmlLoader objektumot globálissá tesszük az osztályon belül.
Teljes kód:
- App.java
package com.example; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.IOException; import java.util.List; /** * JavaFX App */ public class App extends Application { private static Scene scene; //A statikus mainController private static MainController mainController; // globális fxmlLoader: private static FXMLLoader fxmlLoader; @Override public void start(Stage stage) throws IOException { scene = new Scene(loadFXML("mainScene"), 640, 480); mainController = fxmlLoader.getController(); stage.setScene(scene); stage.show(); } @Override public void stop() throws Exception { System.out.println("stop"); List<String> items = mainController.getListView().getItems(); Storage.writeContent(items); } static void setRoot(String fxml) throws IOException { scene.setRoot(loadFXML(fxml)); } private static Parent loadFXML(String fxml) throws IOException { //Eltávolítottuk a típust: fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml")); return fxmlLoader.load(); } public static void main(String[] args) { launch(); } }
A stop() metódusban így már elérhető a MainController osztály.
Esetünkben a MainControllerben a listView private elérésű, ezért készítünk hozzá egy gettert:
- MainController.java
//... public ListView<String> getListView() { return listView; } //...
A fájlba írás a teljesség kedvéért:
- Storage.java
package com.example; import java.io.FileWriter; import java.io.IOException; import java.util.List; public class Storage { public static void writeContent(List<String> content) { try { tryWriteContent(content); } catch (IOException e) { System.err.println(e.getMessage()); } } public static void tryWriteContent(List<String> content) throws IOException { FileWriter writer = new FileWriter("data.txt"); for (String line : content) { writer.write(line + "\n"); } writer.close(); } }
A megvalósítás megfordítható. Ha a stage objektumot globálissá tesszük az App osztályban, a kontrollerben elérhetővé válik, így reagálhatunk a Close eseményre.
A Close esemény
Ha szeretnénk megakadályozni, hogy az ablakbezárás megtörténjen, el kell kapnunk a Close eseményt. Egészítsük ki a start() metódust:
@Override public void start(Stage stage) throws IOException { scene = new Scene(loadFXML("mainScene"), 640, 480); stage.setScene(scene); stage.show(); stage.setOnCloseRequest(e -> { System.out.println("close"); }); }
A lambda kifejezés a következőt helyettesíti:
stage.setOnCloseRequest(new EventHandler<WindowEvent>() { @Override public void handle(WindowEvent event) { System.out.println("close"); } });
Az alkalmazás bezárásának megakadályozása a consume() metódus hívásával lehetséges:
stage.setOnCloseRequest(e -> { System.out.println("close"); e.consume(); });
Ebben az esetben valószínűleg szeretnék valamilyen más eseményre kilépni az alkalmazásból. Kilépéshez használhatjuk a javafx.application.Platform osztályt.
import javafx.application.Platform; //... Platform.exit();