简体   繁体   English

JavaFX:将时间轴的持续时间绑定到属性

[英]JavaFX: Binding Timeline's duration to a property

I have an application that has a combobox with some double values. 我有一个具有一些双值的组合框的应用程序。 The user can select any of the values. 用户可以选择任何值。 The application has a " TimeLine " attached to it that will print a statement on the console. 该应用程序附加了一个“ TimeLine ”,它将在控制台上打印一个语句。 The sscce is below. sscce在下面。 What should happen is that is should print the text of the option selected from the combobox. 应该发生的是应该打印从组合框中选择的选项的文本。 Pls advise. 请建议。

package just.to.test;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TimerSample extends Application  {
    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Text Fonts");

        Group g = new Group();
        Scene scene = new Scene(g, 150, 100);

        ObservableList<Double> data = FXCollections.observableArrayList();

        data.add(5.0);
        data.add(10.0);
        data.add(15.0);
        data.add(20.0);

        ComboBox<Double> timeOptions = new ComboBox<Double>(data);
        timeOptions.getSelectionModel().selectFirst();

        g.getChildren().addAll(timeOptions);

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

        final double timerInterval = timeOptions.getSelectionModel().getSelectedItem();

        KeyFrame keyFrame = new KeyFrame(Duration.seconds(timerInterval),
            new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    System.out.println("This is called every "
                        + timerInterval + " seconds");
                }
            });

        Timeline timerThread = new Timeline(keyFrame);
        timerThread.setCycleCount(Timeline.INDEFINITE);
        timerThread.play();
    }
}

You can't bind a Timeline's duration to a property because the duration of a Keyframe in a Timeline is not a property and you can only bind properties to properties. 您无法将时间轴的持续时间绑定到属性,因为时间轴中的关键帧的持续时间不是属性,您只能将属性绑定到属性。

What you need to do instead is to listen for changes in the value of the combo-box and trigger create new key frames with the new durations when the user selects a new duration. 您需要做的是监听组合框值的变化,并在用户选择新的持续时间时触发创建具有新持续时间的新关键帧。 You also cannot modify the key frames of a running timeline, so you have to stop the timeline before you set the new keyframes for the timeline, then start the timeline after the new keyframes have been set. 您也无法修改正在运行的时间轴的关键帧,因此您必须在设置时间轴的新关键帧之前停止时间轴,然后在设置新关键帧后启动时间轴。

Sample Code 示例代码

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TimerSample extends Application  {
    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        Group g = new Group();
        Scene scene = new Scene(g, 150, 100);

        ComboBox<Double> timerOptions = createTimerOptions(
                0.5, 1.0, 1.5, 2.0
        );
        g.getChildren().addAll(timerOptions);

        createTimer(timerOptions);

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

    private ComboBox<Double> createTimerOptions(double... options) {
        ObservableList<Double> data = FXCollections.observableArrayList();

        for (Double option: options) {
            data.add(option);
        }

        return new ComboBox<Double>(data);
    }

    private void createTimer(ComboBox<Double> timeOptions) {
        final Timeline timer = new Timeline();
        timer.setCycleCount(Timeline.INDEFINITE);

        timeOptions.valueProperty().addListener(new ChangeListener<Double>() {
            @Override
            public void changed(ObservableValue<? extends Double> observable, Double oldValue, Double newValue) {
                resetTimer(timer, newValue);
            }
        });

        timeOptions.getSelectionModel().selectFirst();
    }

    private void resetTimer(Timeline timer, final double timerInterval) {
        KeyFrame keyFrame = new KeyFrame(
            Duration.seconds(timerInterval),
            new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    System.out.println(
                            "This is called every "
                                    + timerInterval
                                    + " seconds"
                    );
                }
            }
        );

        timer.stop();
        timer.getKeyFrames().setAll(
                keyFrame
        );
        timer.play();
    }
}

just wondering if performance wise it is much heavier due to adding a new keyframe with every change in the combobox value. 只是想知道性能是否明显更重,因为在组合框值的每个变化中添加一个新的关键帧。

Don't worry too much about performance in this case - this is a highly efficient operation. 在这种情况下,不要过分担心性能 - 这是一种高效的操作。

There is no other solution than creating new key frames because key frames are immutable objects . 除了创建新的关键帧之外没有其他解决方案,因为关键帧是不可变对象

You could construct key frames up front or place them in something like a LRU cache as you lazily construct them, but in general the additional complexity involved will almost certainly not be worth it. 您可以在前面构建关键帧,或者在懒惰地构建它们时将它们放置在LRU缓存之类的东西中,但通常所涉及的额外复杂性几乎肯定不值得。

All animations have a rateProperty() which can be changed on a running animation! 所有动画都有一个rateProperty() ,可以在正在运行的动画中更改

This seems like a much cleaner solution: 这似乎是一个更清洁的解决方案:

private void createTimer(ComboBox<Double> timeOptions) {
    Timeline timer = new Timeline(
            new KeyFrame(Duration.seconds(1),
            evt-> System.out.println(
                        "This is called every "
                                + timeOptions.getValue()
                                + " seconds"
                ));

    timer.setCycleCount(Timeline.INDEFINITE);
    timer.rateProperty()
         .bind(new SimpleDoubleProperty(1.0)
         .divide(timeOptions.valueProperty()));

    timeOptions.getSelectionModel().selectFirst();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM