【问题标题】:Best way to synchronize countdowntimers in JavaFX在 JavaFX 中同步倒数计时器的最佳方法
【发布时间】:2020-01-10 12:24:01
【问题描述】:

我的程序需要九个倒数计时器。定时器由用户启动。在我的实现中,我为每个启动的计时器创建了一个计时器类。计时器类使用时间线。根据计时器的开始,秒数是异步的。

我不确定如何继续。

我的第一个想法是只为所有倒计时使用一个时间线。我会将所有 stringProperties 放入一个列表中,时间线将更改该属性。我不太确定这是否是一个好方法?

通过一些谷歌,我发现有可以用于此类问题的动画计时器。但我无法理解这些例子。我必须覆盖句柄方法。我应该如何用这个更新我的计时器?

【问题讨论】:

标签: javafx countdowntimer


【解决方案1】:

想法是正确的:使用PauseTransitionTimeLine 等一种动画工具(1) 来更新所有计数器,如以下MRE 所示:

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SyncedCounters extends Application {

    private static final int MAX_COUNT = 100;
    private Map<Label, Integer> counters;
    private VBox countersPane;

    @Override public void start(Stage stage) throws IOException {

        counters = new HashMap<>();
        countersPane = new VBox();
        Button addCounter = new Button("Add Counter");
        addCounter.setOnAction(e->addCounter());
        BorderPane root = new BorderPane(countersPane, null, null, null, addCounter);
        stage.setScene(new Scene(new ScrollPane(root),250,200));
        stage.show();
        update();
    }

    private void update() {

        PauseTransition pause = new PauseTransition(Duration.seconds(1));
        pause.setOnFinished(event ->{
            updateCounters();
            pause.play();
        });
        pause.play();
    }

    private void addCounter() {

        Label label = new Label(String.valueOf(MAX_COUNT));
        label.setAlignment(Pos.CENTER);
        label.setPrefSize(150, 25);
        counters.put(label, MAX_COUNT);
        countersPane.getChildren().add(label);
    }


    private void updateCounters() {
        for(Label l : counters.keySet()){
            int counterValue = counters.get(l);
            if(counterValue > 0 ){
                counterValue--;
                l.setText(String.valueOf(counterValue));
                counters.put(l, counterValue);
            }
        }
    }

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


(1) 要使用TimeLine 而不是PauseTransition,请将update() 更改为:
void update() {

    Timeline timeline = new Timeline();
    timeline.setCycleCount(Animation.INDEFINITE);

    KeyFrame keyFrame = new KeyFrame(
            Duration.seconds(1),
            event -> {updateCounters();}
    );

    timeline.stop();
    timeline.getKeyFrames().clear();
    timeline.getKeyFrames().add(keyFrame);
    timeline.play();
}

【讨论】:

  • 谢谢你,这是一个很好的答案!它对我有帮助。
【解决方案2】:
If I understand you correctly, here is a sample program that demos `Timeline`. It uses two `Timelines` to count down to zero. **Update:** I think what you are saying is that you want the timers' numbers to change at the same time. If so update below.

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @author rstein
 */
public class App extends Application {

    Timeline timeline1;
    Timeline timeline2;

    Button button1 = new Button("Start");
    Button button2 = new Button("Start");

    @Override
    public void start(final Stage primaryStage) {

        IntegerProperty counter1 = new SimpleIntegerProperty(9);
        timeline1 = new Timeline(new KeyFrame(Duration.seconds(1), (t) ->
        {
            counter1.set(counter1.get() - 1);
            if(counter1.get() == 0)
            {
                timeline1.stop();
                System.out.println("Done!");
                button1.setText("Done");
            }         
            System.out.println("counter 1: " + counter1.get());            
        }));     
        timeline1.setCycleCount(Timeline.INDEFINITE);        
        button1.setOnAction((t) ->
        {
            switch(button1.getText())
            {
                case "Start":
                    timeline1.play();
                    button1.setText("Pause");
                    break;
                case "Pause":
                    timeline1.stop();
                    button1.setText("Start");
                    break;
            }
        });
        Label label1 = new Label();
        label1.textProperty().bind(counter1.asString());


        IntegerProperty counter2 = new SimpleIntegerProperty(9);
        timeline2 = new Timeline(new KeyFrame(Duration.seconds(1), (t) ->
        {
            counter2.set(counter2.get() - 1);
            if(counter2.get() == 0)
            {
                timeline2.stop();
                System.out.println("Done!");
                button2.setText("Done");
            }         
            System.out.println("counter 1: " + counter1.get());            
        }));     
        timeline2.setCycleCount(Timeline.INDEFINITE);

        button2.setOnAction((t) ->
        {
            switch(button2.getText())
            {
                case "Start":
                    timeline2.play();
                    button2.setText("Pause");
                    break;
                case "Pause":
                    timeline2.stop();
                    button2.setText("Start");
                    break;
            }
        });
        Label label2 = new Label();
        label2.textProperty().bind(counter2.asString());

        VBox root = new VBox(new HBox(button1, label1), new HBox(button2, label2));
        Scene scene = new Scene(root, 300, 200);

        primaryStage.setTitle(this.getClass().getSimpleName());
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    /**
     * @param args the command line arguments
     */
    public static void main(final String[] args) {
        Application.launch(args);
    }
}

更新

在更新中,只有一个Timeline。如果两个计数器都在运行,则它们会同时更改。

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * @author Sedrick
 */
public class App extends Application {

    Timeline timeline;

    Button button1 = new Button("Start");
    Button button2 = new Button("Start");
    Boolean btn1IsRunning = false;
    Boolean btn2IsRunning = false;


    @Override
    public void start(final Stage primaryStage) {

        IntegerProperty counter1 = new SimpleIntegerProperty(9);
        IntegerProperty counter2 = new SimpleIntegerProperty(9);

        timeline = new Timeline(new KeyFrame(Duration.seconds(1), (t) ->
        {
            if(btn1IsRunning && !button1.getText().equals("Done"))
            {
                counter1.set(counter1.get() - 1);
            }

            if(counter1.get() == 0)
            {
                System.out.println("Done!");
                button1.setText("Done");
            }         
            System.out.println("counter 1: " + counter1.get());     

            if(btn2IsRunning && !button2.getText().equals("Done"))
            {
                counter2.set(counter2.get() - 1);
            }

            if(counter2.get() == 0)
            {

                System.out.println("Done!");
                button2.setText("Done");
            }         
            System.out.println("counter 2: " + counter2.get());   

            if(button1.getText().equals("Done")&& button2.getText().equals("Done"))
            {
                timeline.stop();
            }
        }));     
        timeline.setCycleCount(Timeline.INDEFINITE);        
        button1.setOnAction((t) ->
        {
            switch(button1.getText())
            {
                case "Start":
                    if(timeline.getStatus() != Timeline.Status.RUNNING)
                    {
                        timeline.play();
                    }
                    btn1IsRunning = true;
                    button1.setText("Pause");
                    break;
                case "Pause":
                    btn1IsRunning = false;
                    button1.setText("Start");
                    break;
            }
        });
        Label label1 = new Label();
        label1.textProperty().bind(counter1.asString());

        button2.setOnAction((t) ->
        {
            switch(button2.getText())
            {
                case "Start":
                    if(timeline.getStatus() != Timeline.Status.RUNNING)
                    {
                        timeline.play();
                    }
                    btn2IsRunning = true;
                    button2.setText("Pause");
                    break;
                case "Pause":
                    btn2IsRunning = false;
                    button2.setText("Start");
                    break;
            }
        });
        Label label2 = new Label();
        label2.textProperty().bind(counter2.asString());

        VBox root = new VBox(new HBox(button1, label1), new HBox(button2, label2));
        Scene scene = new Scene(root, 300, 200);

        primaryStage.setTitle(this.getClass().getSimpleName());
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    /**
     * @param args the command line arguments
     */
    public static void main(final String[] args) {
        Application.launch(args);
    }
}

【讨论】:

  • 抱歉英语不是我的母语。让我们举个例子:当你启动第一个计时器时,timeline1 启动并更新文本。例如,您现在启动 timneline2 时间线为 1.5 秒。第一个计时器显示 8 第二个 9 然后 0.5 秒后第一个计时器将下降到 7 而第二个计时器仍然显示 9。他们将更改他们的数量 asyncronus。但我想要的是,当计时器 1 下降 1 秒时,计时器 2 应该在“相同”的时刻下降 1 秒。如果您有很多计时器并且它们异步运行,这看起来很奇怪。
  • 我想我明白了。午饭后我会更新代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多