JavaFX Idle Detection 2


I am trying to create a simple Java transaction application with JavaFX as the UI.
Now I want to detect a custom idle state from my application that has 1 main stage and many scenes.

Example: If the user is idle for 3 minutes, return to the main menu.

I have already tried several examples on the internet on how to detect the idle state of JavaFX, but what I found is always a function to detect the idle state that covers all scenes - a method that (I think) is dangerous for a transaction application ( for example: applications discover an idle state in the middle of a transaction process).
Is it possible to determine the idle state of the user on each separate scene? as?
Thank you.

EDIT:

Examples I've already tried:

http://tomasmikula.github.io/blog/2014/06/04/timers-in-javafx-and-reactfx.html

and

http://ochafik.com/blog/?p=98

+3


source to share


1 answer


I really don't understand what you are doing about transactional behavior. Transactions are about data guarantees, and your transactional behavior should be defined at the data layer and should not affect what happens in the user interface. In other words, your atomic behavior should terminate or rollback even if the UI is reset due to the absence of a user.

Maybe this will help. (Note: I've used Java 8 code in these examples, but you can make it JavaF 2.2 compliant pretty easily if you need to.) This follows Thomas Mikula's general approach in that he usesTimeline

to implement idle checking. I haven't used Tomas's FX Timer chip, but you can do it if you like. This class encapsulates a monitor for whether the user is free. You can register any node (or scene) and event type: if an event of this type occurs on this node (or scene), the user has decided that he is not idle. If the specified time elapses without any logged events, the runnable being executed (on the FX application thread) is executed. This gives you the ability to create multiple monitors, if desired, and register one or more nodes with each.

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.Scene;

import javafx.util.Duration;

public class IdleMonitor {

    private final Timeline idleTimeline ;

    private final EventHandler<Event> userEventHandler ;

    public IdleMonitor(Duration idleTime, Runnable notifier, boolean startMonitoring) {
        idleTimeline = new Timeline(new KeyFrame(idleTime, e -> notifier.run()));
        idleTimeline.setCycleCount(Animation.INDEFINITE);

        userEventHandler = e -> notIdle() ; 

        if (startMonitoring) {
            startMonitoring();
        }
    }

    public IdleMonitor(Duration idleTime, Runnable notifier) {
        this(idleTime, notifier, false);
    }

    public void register(Scene scene, EventType<? extends Event> eventType) {
        scene.addEventFilter(eventType, userEventHandler);
    }

    public void register(Node node, EventType<? extends Event> eventType) {
        node.addEventFilter(eventType, userEventHandler);
    }

    public void unregister(Scene scene, EventType<? extends Event> eventType) {
        scene.removeEventFilter(eventType, userEventHandler);
    }

    public void unregister(Node node, EventType<? extends Event> eventType) {
        node.removeEventFilter(eventType, userEventHandler);
    }

    public void notIdle() {
        if (idleTimeline.getStatus() == Animation.Status.RUNNING) {
            idleTimeline.playFromStart();
        }
    }

    public void startMonitoring() {
        idleTimeline.playFromStart();
    }

    public void stopMonitoring() {
        idleTimeline.stop();
    }
}

      

Here's a test. Start buttons are possibly stand-ins for login. The main user interface has a tab bar with two tabs: each individual tab starts with its own start button, and then the main content has a label, a text box and a button.



The content of each tab has an associated (short, for testing) idle monitor. Any event on the tab content will be reset by an unoccupied monitor, but events outside of the tab content will not be reset. There is also a "global" window-wide idle monitor that resets the entire UI after 30 seconds.

Please note that the data is saved: i.e. if you time out due to idle, whatever text you enter in the textbox is saved correctly. This is why I think the problem with "transactions" doesn't matter at all.

import javafx.application.Application;
import javafx.event.Event;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class IdleTest extends Application {

    @Override
    public void start(Stage primaryStage) {

        StackPane root = new StackPane();

        Parent mainUI = buildMainUI();
        Scene scene = new Scene(root, 350, 150);
        Parent startUI = buildStartUI(() -> root.getChildren().setAll(mainUI));
        root.getChildren().add(startUI);

        IdleMonitor idleMonitor = new IdleMonitor(Duration.seconds(30),
                () -> root.getChildren().setAll(startUI), true);
        idleMonitor.register(scene, Event.ANY);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private Parent buildStartUI(Runnable start) {
        Button button = new Button("Start");
        button.setOnAction(e -> start.run());
        StackPane root = new StackPane(button);
        return root ;
    }

    private Parent buildMainUI() {
        TabPane tabPane = new TabPane();
        Tab tab1 = new Tab("One");
        Parent tab1Content = buildTabUI("Tab 1");
        Parent tab1StartContent = buildStartUI(() -> tab1.setContent(tab1Content));
        tab1.setContent(tab1StartContent);
        IdleMonitor tab1IdleMonitor = new IdleMonitor(Duration.seconds(5), 
                () -> tab1.setContent(tab1StartContent), true);
        tab1IdleMonitor.register(tab1Content, Event.ANY);

        Tab tab2 = new Tab("Two");
        Parent tab2Content = buildTabUI("Tab 2") ;
        Parent tab2StartContent = buildStartUI(() -> tab2.setContent(tab2Content));
        tab2.setContent(tab2StartContent);
        IdleMonitor tab2IdleMonitor = new IdleMonitor(Duration.seconds(10),
                () -> tab2.setContent(tab2StartContent), true);
        tab2IdleMonitor.register(tab2Content, Event.ANY);

        tabPane.getTabs().addAll(tab1, tab2);
        return tabPane ;
    }

    private Parent buildTabUI(String text) {
        Button button = new Button("Click here");
        button.setOnAction(e -> System.out.println("Click in "+text));
        VBox content = new VBox(10, new Label(text), new TextField(), button);
        content.setAlignment(Pos.CENTER);
        return content ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

      

+9


source







All Articles