JavaFX - цикл через TextFields в GridPane и обновление текста с паузой

У меня есть GridPane с 20 пустыми текстовыми полями. Я хочу перебирать каждое текстовое поле и обновлять текст в каждом со значениями из ArrayList с паузой в 1 секунду между каждым. Я не могу понять это.

Я создаю GridPane так:

        GridPane grid = new GridPane();
        
        Scene drawing = new Scene(new VBox(grid), 500, 200);
        primaryStage.setScene(drawing);
        
        for (int i = 0; i < 2; ++i) {
            for (int j = 0; j < 10; ++j) {              
                TextField tf = new TextField();
                tf.setPrefHeight(50);
                tf.setPrefWidth(50);
                tf.setAlignment(Pos.CENTER);
                tf.setEditable(false);
                grid.add(tf, j, i);
            }
        }

Теперь я хочу просмотреть каждое текстовое поле и добавить текст с паузой между ними. Использование Thread.sleep() в цикле приводит к сбою приложения. Я пробовал PauseTransition следующим образом:

ArrayList<Integer> numsDrawn= game.draw();
int count = 0;
for (Node node : grid.getChildren()) {
            PauseTransition pause = new PauseTransition(Duration.seconds(1));
            pause.setOnFinished(e -> ((TextField)node).setText(Integer.toString(numsDrawn.get(count))));
            pause.play();
            count++;
        }

Но я получаю сообщение об ошибке Количество локальных переменных, определенное в объемлющей области, должно быть окончательным или фактически окончательным.

Счетчик должен иметь возможность изменяться, чтобы я мог перебирать список numsDrawn и добавлять разный текст в каждое TextField. Я попытался создать отдельный обработчик событий вместо лямбды, но получил ту же ошибку со счетом.

Если кто-то может дать совет о том, как сделать эту, казалось бы, простую задачу, я был бы очень признателен.


person shrapnel360    schedule 04.10.2020    source источник


Ответы (3)


Я бы рекомендовал Timeline в этой ситуации. Установите продолжительность на одну секунду и количество циклов на число TextFields в GridPane.

import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class App extends Application  {

    Timeline timeline;
    AtomicInteger counter = new AtomicInteger();
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        GridPane gridPane = new GridPane();
        gridPane.add(new TextField(), 0, 0);
        gridPane.add(new TextField(), 0, 1);
        gridPane.add(new TextField(), 0, 2);
        gridPane.add(new TextField(), 0, 3);
        gridPane.add(new TextField(), 0, 4);
        gridPane.add(new TextField(), 0, 5);
        gridPane.add(new TextField(), 0, 6);
        gridPane.add(new TextField(), 0, 7);
        gridPane.setMaxSize(Control.USE_PREF_SIZE, Control.USE_PREF_SIZE);
        
        timeline = new Timeline(new KeyFrame(Duration.seconds(1), (ActionEvent t) -> {
            System.out.println(counter.get());
            TextField tempTextField = (TextField)gridPane.getChildren().get(counter.get());
            tempTextField.setText(Integer.toString(counter.getAndIncrement()));           
        }));
        timeline.setCycleCount(gridPane.getChildren().size());
        
        Button btnStartTimeline = new Button("Start Timeline");
        btnStartTimeline.setOnAction((t) -> {
            timeline.play();
        });
        
        VBox root = new VBox(gridPane, btnStartTimeline);
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root, 700, 700);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public static void main(String[] args) {
        Application.launch(args);
    }
    
    
}
person Sedrick    schedule 04.10.2020
comment
Спасибо!! В чем преимущество использования временной шкалы по сравнению с простым запуском нового потока? - person shrapnel360; 10.10.2020
comment
Вы могли бы использовать Thread, но если вы это сделали, вы должны использовать Task или Service. Task, Service и Timeline предназначены для JavaFX. Timeline лучше всего использовать из трех при изменении Node. Два других лучше подходят для фоновой работы, которая не изменяет Node(s). Могут быть и другие идеи из Animation API, которые можно использовать при изменении Nodes. - person Sedrick; 11.10.2020

Согласно сообщению об ошибке, вы должны передать переменную final методу numsDrawn.get, поэтому я бы попробовал следующее:

ArrayList<Integer> numsDrawn= game.draw();
int count = 0;
for (Node node : grid.getChildren()) {
    PauseTransition pause = new PauseTransition(Duration.seconds(1));
    final int countFinal = count;
    pause.setOnFinished(e -> ((TextField)node).setText(Integer.toString(numsDrawn.get(countFinal))));
    pause.play();
    count++;
}
person Couper    schedule 04.10.2020

Так что я разобрался сам. При создании сетки с текстовыми полями я также добавляю каждое текстовое поле в ArrayList tfs, чтобы иметь доступ к каждому полю по отдельности, чтобы позже добавить текст. Затем я создаю новый поток для добавления текста в каждое поле из numsDrawn следующим образом:

new Thread(() -> {
            for (int i = 0; i < 20; ++i) {
                final int j = i;
                Platform.runLater(() -> tfs.get(j).setText(Integer.toString(numsDrawn.get(j))));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        }).start();
person shrapnel360    schedule 04.10.2020
comment
Запуск нового потока не требуется, если вы используете Platform.runLater. В любом случае проверьте, решит ли мой ответ вашу проблему. - person Couper; 05.10.2020