JavaFX Events und Property-Änderungen

in Arbeit ...

Es gibt im Prinzip 2 Möglichkeiten, auf Zustandsänderungen zu reagieren:

  • Auf Events (Ereignisse: Mausbewegung, Mausklick, Keyboard, Touch, Action v. UI-Elementen, etc.) direkt reagieren (EventHandlers, EventFilters)

  • Bei Änderungen von JavaFX-Properties (JavaFX-eigenes Konzept) als Interessent anmelden (Listener – es können beliebig viele Listeners angemeldet werden) und von jeder Änderung informiert werden. Es gibt ChangeListener – diese werden bei jeder Änderung der interessierenden Property aktiv – und InvalidationListener (werden nur aktiv, wenn eine – üblicherweise berechnete – Property neu berechnet werden muss).

In vielen Fällen stehen beide Möglichkeiten zur Verfügung, da ein Ereignis sehr oft auch die Änderung einer Property zur Folge hat.

Oft werden "Roh"-Events (z.B. Mausklick) in abstraktere Events umgewandelt z.B. Auslöen eines Action-Events, wenn ein Button, Menü-Item, Checkbox etc. mit Mausklick oder durch Keyboard-Shortcuts etc. aktiviert wurde.

Als Beispiel ein Maus-Drag des Fensterrandes führt zu einer Änderung der Fenstergröße, die wiederum eine Property-Änderung der WidthProperty und/oder HeightProperty auslöst, wovon alle angemeldeten Listeners durch automatischen Aufruf von deren in den beiden Listener-Interfaces definierten Methoden changed(ObservableValue<? extends T> observable, T oldValue, T newValue) bzw. invalidated(Observable observable) informiert werden (invalidated(…​) nur dann, wenn die Property neu invalid geworden ist).

entweder durch Benutzerinteraktion oder durch Änderungen, die  von Software-Komponenten.

Events werden entweder direkt über Eingabe-Hardware ausgelöst (siehe InputEvent (JavaFX 19): KeyEvent, MouseEvent, TouchEvent, …​) oder durch Zustandsänderungen von Software-Komponenten (siehe z.B. Event (JavaFX 19): ActionEvent, WindowEvent, …​).

https://code.makery.ch/blog/javafx-8-event-handling-examples/ [JavaFX 8 Event Handling Examples | code.makery.ch] …​ 2023-05-01

Refreshing Elements, Ensuring automatic refresh of Lists etc.: How to force refresh the Scene in JavaFX (and 3 reasons you don’t have to) – Eden Coding …​ 1.5.2021, 11:00:07

JavaFX - Event Handling - Tutorialspoint …​ 1.5.2021, 13:38:12

https://taylorial.com/cs1021/EventHandlingFX.htm [2] …​ 1.5.2021, 14:45:18

https://docs.oracle.com/javase/8/javafx/events-tutorial/events.htm#:~:text=In JavaFX applications%2C events are,event and provide a response. [Part I: Handling Events (Release 8)] …​ 2023-02-16

https://docs.oracle.com/javafx/2/events/handlers.htm [Handling JavaFX Events: Working with Event Handlers | JavaFX 2 Tutorials and Documentation] …​ 2023-02-16

https://www.tutorialspoint.com/javafx/javafx_event_handling.htm [JavaFX - Event Handling] …​ 2023-02-16

https://www.javatpoint.com/javafx-event-handlers [JavaFX Event Handlers - javatpoint] …​ 2023-02-16

https://falconbyte.net/java-javafx-eventhandler.php [JavaFX Tutorial: Event Handler

falconbyte.net] …​ 2023-02-16

https://edencoding.com/javafx-events/ [A Definitive Guide To JavaFX Events – Eden Coding] …​ 2023-02-16

http://www.java2s.com/Tutorials/Java/JavaFX/1100__JavaFX_Events.htm [JavaFX Tutorial - JavaFX Events] …​ 2023-02-16

https://skeoop.github.io/javafx/Event-Handling.pdf [Event Handling in JavaFX] …​ 2023-02-16

https://se-education.org/guides/tutorials/javaFxPart3.html [JavaFX tutorial part 3 – Interacting with the user] …​ 2023-02-16

https://stackoverflow.com/questions/74889354/javafx-question-on-terms-target-and-source [java - JavaFX question on terms "target" and "source" - Stack Overflow] …​ 2023-02-20 mit illustrativem Beispiel:

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

    private int indent;
    private final EventHandler<Event> filter = event -> {
        System.out.printf(
                "%ssource = %s, target = %s%n",
                " ".repeat(indent),
                event.getSource(),
                event.getTarget()
        );
        if (event.getSource() != event.getTarget()) {
            indent += 4;
        } else {
            System.out.println(" ".repeat(indent) + "CAPTURING PHASE COMPLETE -- BUBBLING PHASE STARTING");
        }
    };
    private final EventHandler<Event> handler = event -> {
        System.out.printf(
                "%ssource = %s, target = %s%n",
                " ".repeat(indent),
                event.getSource(),
                event.getTarget()
        );
        indent -= 4;
    };

    @Override
    public void start(Stage primaryStage) {
        Button targetNode = new Button("Click me!");
        targetNode.addEventFilter(ActionEvent.ACTION, filter);
        targetNode.addEventHandler(ActionEvent.ACTION, handler);
        targetNode.setId("targetNode");

        StackPane layoutTwo = new StackPane(targetNode);
        layoutTwo.addEventFilter(ActionEvent.ACTION, filter);
        layoutTwo.addEventHandler(ActionEvent.ACTION, handler);
        layoutTwo.setId("layoutTwo");

        StackPane layoutOne = new StackPane(layoutTwo);
        layoutOne.addEventFilter(ActionEvent.ACTION, filter);
        layoutOne.addEventHandler(ActionEvent.ACTION, handler);
        layoutOne.setId("layoutOne");

        StackPane rootLayout = new StackPane(layoutOne);
        rootLayout.addEventFilter(ActionEvent.ACTION, filter);
        rootLayout.addEventHandler(ActionEvent.ACTION, handler);
        rootLayout.setId("rootLayout");

        Scene scene = new Scene(rootLayout, 500, 300);
        scene.addEventFilter(ActionEvent.ACTION, filter);
        scene.addEventHandler(ActionEvent.ACTION, handler);

        primaryStage.setScene(scene);
        primaryStage.addEventFilter(ActionEvent.ACTION, filter);
        primaryStage.addEventHandler(ActionEvent.ACTION, handler);
        primaryStage.show();
    }
}

2. ToDos

Whenever an event happens, it follows a process to determine which node in the scene graph should handle the event. The process takes these steps:

  • Target selection

  • Route construction

  • Event capturing ← filters are triggered here

  • Event bubbling ← handlers are triggered here

Target Selection: Say that your scene contains a pane with a circle. If you click on the circle, the circle becomes the event target.

Route Construction: Next, JavaFX creates a route (or an event dispatch chain). In our example the chain would look like stage → scene → pane → circle

Event Capturing: The event gets carried through every event filter on the chain. As soon as one of the filters calls consume(), the chain stops and that node becomes the target. If no filter calls consume() the end of the chain (the circle) remains the target.

Event Bubbling: Next, the event get pushed through the chain again, but this time from the event target to the stage. So if the pane event filter called consume(), the following event handlers will be hit: pane → scene → stage

So the difference is not only when these handlers get activated, but also that event filters can prevent child nodes from receiving events.