6


UIとしてJavaFXを使用した単純なJavaトランザクションアプリを作成しようとしています。
私が今やりたいことは、1 つのプライマリ ステージと多くのシーンを持つアプリケーションからユーザーのアイドル状態を検出することです。

例 : ユーザーが 3 分間アイドル状態の場合、メイン メニューに戻ります。

JavaFXのアイドル状態を検出する方法について、すでにWebでいくつかの例を試していますが、私が見つけたのは常に-すべてのシーンで発生している1つの関数のアイドル状態の検出-トランザクションアプリにとって危険な(と思う)メソッドです(例:アプリ検出トランザクション処理中のアイドル状態)。
すべてのシーンでユーザーのアイドル状態を検出することは可能ですか? どうやって?
ありがとう。

編集 :

私がすでに試した例:

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

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

4

1 に答える 1

13

トランザクション動作についてあなたが言っている点がよくわかりません。トランザクションはデータに関する保証に関係しており、トランザクションの動作はデータ レベルで定義する必要があり、UI で起こっていることに影響されないようにする必要があります。つまり、ユーザーがアイドル状態であるために UI がリセットされた場合でも、アトミックな動作は完了またはロールバックする必要があります。

たぶん、これは役立つでしょう。(これらの例では Java 8 コードを使用しましたが、必要に応じて JavaF 2.2 に準拠させるのはかなり簡単です。)これはTimeline、アイドル チェックを実装するために a を使用するという点で、Tomas Mikula の一般的なアプローチに従います。私は Tomas の FX タイマー ラッパーを使用しませんでしたが、必要に応じて使用することもできます。このクラスは、ユーザーがアイドル状態かどうかのモニターをカプセル化します。任意のノード (またはシーン) とイベントの種類を登録できます。そのノード (またはシーン) でその種類のイベントが発生した場合、ユーザーはアイドル状態ではないと判断されます。登録されたイベントが発生せずに指定された時間が経過すると、提供されたランナブルが (FX アプリケーション スレッドで) 実行されます。これにより、必要に応じて複数のモニターを作成し、それぞれに 1 つ以上のノードを登録する柔軟性が得られます。

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();
    }
}

これがテストです。「開始」ボタンはおそらくログイン用の代用です。メイン UI には 2 つのタブを持つタブ ペインがあります。個々のタブはそれぞれ独自の「開始」ボタンで始まり、メイン コンテンツにはラベル、テキスト フィールド、およびボタンがあります。 .

それぞれのタブのコンテンツには、(テスト用の短い) アイドル モニターが関連付けられています。タブのコンテンツでイベントが発生すると、アイドル モニターはリセットされますが、タブ コンテンツ以外のイベントではリセットされません。30 秒後に UI 全体をリセットするウィンドウ全体の「グローバル」アイドル モニターもあります。

データが保持されることに注意してください。つまり、アイドル状態でタイムアウトした場合、テキスト フィールドに入力したテキストは適切に保持されます。これが、「トランザクション」の問題はまったく問題にすべきではないと私が考える理由です。

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);
    }
}
于 2014-11-27T04:44:12.583 に答える