Хобрук: Ваш путь к мастерству в программировании

Вернуть значение после завершения временной шкалы JavaFX

Я использую временную шкалу в JavaFX для обратного отсчета Label:

timeline.setCycleCount(6);
timeline.play();

И я хочу вернуть значение после завершения временной шкалы:

return true;

Однако кажется, что значение возвращается немедленно, а временная шкала работает параллельно. Как я могу дождаться, пока временная шкала завершит обратный отсчет, а затем вернуть значение, не блокируя временную шкалу?

РЕДАКТИРОВАТЬ:

Чтобы было понятнее, я уже пробовал:

new Thread(() -> {
    timeline.play();
}).start();

while(!finished){ // finished is set to true, when the countdown is <=0

}
return true;

(Это решение не обновляет обратный отсчет.)

РЕДАКТИРОВАТЬ 2:

Вот минимальный, полный и проверяемый пример:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;


public class CountdownTest extends Application {

    private Label CountdownLabel;
    private int Ctime;

    @Override
    public void start(Stage primaryStage) {

        CountdownLabel=new Label(Ctime+"");

        StackPane root = new StackPane();
        root.getChildren().add(CountdownLabel);

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Countdown Test");
        primaryStage.setScene(scene);
        primaryStage.show();

        Ctime=5;

        if(myCountdown()){
            CountdownLabel.setText("COUNTDOWN FINISHED");
        }
    }

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

    public boolean myCountdown(){
         final Timeline timeline = new Timeline(
                            new KeyFrame(
                                    Duration.millis(1000),
                                    event -> {
                                    CountdownLabel.setText(Ctime+"");
                                    Ctime--;

                                    }

                            )
                    );
         timeline.setCycleCount(6);
         timeline.play();
    return true;
    }

}

Вы можете видеть, что он сначала показывает «ОБРАТНЫЙ ОТЧЕТ ЗАВЕРШЕН» и ведет обратный отсчет до 0 вместо того, чтобы начинать обратный отсчет и обратный отсчет до «ОБРАТНЫЙ ОТЧЕТ ЗАВЕРШЕН».

27.08.2018

  • Предоставьте минимально воспроизводимый пример, демонстрирующий проблему. 27.08.2018
  • Я добавил MCVE, чтобы сделать его более понятным. 27.08.2018
  • хорошо :) просто (не имеет отношения к вашей проблеме): пожалуйста, изучите соглашения об именах Java и придерживайтесь их 27.08.2018
  • вы просто неправильно используете временную шкалу, как уже объяснялось в ответах ... почему бы вам не включить предложения и не показать нам, почему они не соответствуют вашим требованиям? 27.08.2018

Ответы:



2

Если вы действительно хотите дождаться завершения временной шкалы, вы можете использовать CountDownLatch или Semaphore вместе с setOnFinished. Что-то вроде следующего должно работать:

CountDownLatch latch = new CountDownLatch(1);
timeline.setCycleCount(6);
timeline.setOnFinished(event -> latch.countDown());
timeline.play();
latch.await();
return true;
27.08.2018

3

Вы пытаетесь дождаться в одном потоке результата работы в другом потоке. Именно для этого была создана синхронизация! Например. java.util.concurrent.Semaphore:

public boolean waitForTimeline()  {
    Semaphore semaphore = new Semaphore(0);

    System.out.println("starting timeline");
    Timeline t = new Timeline();
    t.getKeyFrames().add(new KeyFrame(Duration.seconds(2)));
    t.setOnFinished((e)-> {
        System.out.println("releasing semaphore"); 
        semaphore.release();
    });
    t.play();

    System.out.println("waiting for timeline to end");
    try {
        semaphore.acquire();
    } catch (InterruptedException ex) {
        ex.printStackTrace();
        return false;
    }
    return true;
}

Но обратите внимание, что вы не можете запустить этот метод в «потоке приложения JavaFX», так как он будет блокировать обновления пользовательского интерфейса. Запустите его в отдельном потоке:

new Thread(()-> { 
    System.out.println("returned from timeline with " + waitForTimeline()); 
}).start();

или, лучше, вместо этого создайте прослушиватель с логикой, которую вы делаете после return, и вызовите этот прослушиватель из t.setOnFinished(). Для вашего примера это будет:

public void myCountdown(Runnable onSuccess){
    //...
    timeline.setOnFinished((e)-> {
        onSuccess.run();
    });
}

и соответствующий вызов:

myCountdown(()->{
    CountdownLabel.setText("COUNTDOWN FINISHED");
});
27.08.2018
  • Имхо, использование Consumer<Boolean> было бы ближе к исходному коду (вы передаете значение готовому обработчику), но в этом случае Runnable должно помочь... (+1) 27.08.2018

  • 4

    Используйте setOnFinished Animation в дополнение к setOnFinished KeyFrame, которые у вас есть (Timeline — это Animation). Я изменил ваш MCVE, чтобы показать, как это сделать для вашего случая:

    public class CountdownTest extends Application {
    
        private Label countdownLabel;
        private int ctime = 5;
    
        @Override
        public void start(Stage primaryStage) {
            countdownLabel = new Label();
    
            StackPane root = new StackPane();
            root.getChildren().add(countdownLabel);
    
            Scene scene = new Scene(root, 300, 250);
    
            primaryStage.setTitle("Countdown Test");
            primaryStage.setScene(scene);
            primaryStage.show();
    
            myCountdown();
        }
    
        public void myCountdown() {
            final Timeline timeline = new Timeline(new KeyFrame(Duration.millis(1000),
                event -> {
                    countdownLabel.setText(ctime + "");
                    ctime--;
                }
            ));
            timeline.setCycleCount(ctime + 1);
            timeline.setOnFinished(e -> countdownLabel.setText("COUNTDOWN FINISHED"));
            timeline.play();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    В каждом цикле будет выполняться setOnFinished KeyFrame, уменьшая счет на 1. Когда вся анимация завершится (все ее циклы), будет выполнено setOnFinished Timeline.

    Примечания:

    • Используйте переменную ctime для расчета количества циклов, чтобы они совпадали.
    • Используйте соглашения об именах Java: идентификаторы (имена) начинаются с нижнего регистра.
    • Делайте правильный отступ в коде, чтобы его было легче читать.
    27.08.2018
    Новые материалы

    Введение в контекст React
    В этом посте мы поговорим о Context API, который был представлен в React 16, и о том, как вы можете их использовать. Что такое контекст? Глядя на определение из react docs , оно..

    Шлюз с лицензией OSS, совместимый с Apollo Federation v2, появится в WunderGraph
    Сегодня мы рады сообщить, что мы сотрудничаем с поддерживаемой YC Tailor Technologies, Inc. для внедрения Apollo Federation v2. Реализация будет лицензирована MIT (Engine) и Apache 2.0..

    Это оно
    Ну, я официально уволился с работы! На этой неделе я буду лихорадочно выполнять последние требования Думающего , чтобы я мог сосредоточиться на поиске работы. Что именно это значит?..

    7 полезных библиотек JavaScript, которые вы должны использовать в своем следующем проекте
    Усильте свою разработку JavaScript Есть поговорка «Не нужно изобретать велосипед». Библиотеки — лучший тому пример. Это поможет вам написать сложные и трудоемкие функции простым способом...

    Базовое руководство по переносу концепций обучения в глубокое обучение
    Обзор По мере того, как машинное обучение становится все более мощным и продвинутым, модели, обеспечивающие эту расширенную возможность, становятся все больше и начинают требовать огромного..

    C в C.R.U.D с использованием React-Redux
    Если вы использовали React, возможно, вы знакомы с головной болью, связанной с обратным потоком данных. Передача состояния реквизитам от родительских компонентов к дочерним компонентам может..

    5 обязательных элементов современного инструмента конвейера данных
    В цифровом мире предприятия используют конвейеры данных для перемещения, преобразования и хранения огромных объемов данных. Эти конвейеры составляют основу бизнес-аналитики и играют..