JavaFX – 循环遍历 GridPane 中的 TextFields 并在暂停时更新文本

huangapple go评论73阅读模式
英文:

JavaFX - Loop through TextFields in a GridPane and update text with pause

问题

我有一个包含20个空文本字段的GridPane我想要循环遍历每个文本字段并且在每个字段中使用来自ArrayList的值来更新文本每次之间暂停约1秒我无法解决这个问题

我像这样创建了GridPane

```java
        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++;
}

但是我收到错误消息Local variable count defined in an enclosing scope must be final or effectively final

count必须能够更改,以便我可以遍历numsDrawn列表并向每个TextField添加不同的文本。我尝试过创建一个单独的事件处理程序而不是lambda,但是仍然收到相同的count错误。

如果有人能够就这个看似简单的任务提供建议,我将非常感激。


<details>
<summary>英文:</summary>

I have a GridPane with 20 empty text fields. I want to loop through each textfield and update the text in each with values from an ArrayList, with ~1 second pause in between each. I can&#39;t figure it out.

I create the GridPane like so:

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

I now want to go through each textbox and add text with a pause in between. Using Thread.sleep() in a loop is causing the application to crash. I&#39;ve tried PauseTransition like this:

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++;
}

But I am getting the error *Local variable count defined in an enclosing scope must be final or effectively final*.

Count has to be able to change so I can iterate through the numsDrawn list and add different text to each TextField. I&#39;ve tried creating a separate event handler instead of a lambda, but getting the same error with count.

If someone could offer advice on how to do this seemingly simple task, I&#39;d greatly appreciate it.

</details>


# 答案1
**得分**: 1

我会建议在这种情况下使用 `Timeline`。将持续时间设置为一秒,将循环次数设置为 `GridPane` 中的 `TextFields` 数量。

```java
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);
    }
}
英文:

I would recommend Timeline in this situation. Set the duration to one second and the cycle count to the number of TextFields in the 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) -&gt; {
            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(&quot;Start Timeline&quot;);
        btnStartTimeline.setOnAction((t) -&gt; {
            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);
    }
    
    
}

答案2

得分: 0

根据错误信息,你应该将一个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++;
}
英文:

According to the error message you should pass a final variable to the numsDrawn.get method, so I would try this:

ArrayList&lt;Integer&gt; 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 -&gt; ((TextField)node).setText(Integer.toString(numsDrawn.get(countFinal))));
    pause.play();
    count++;
}

答案3

得分: 0

我自己解决了这个问题。在创建带有文本字段的网格时,我还将每个文本字段添加到一个名为 tfs 的 ArrayList 中,这样我就可以单独访问每个字段以稍后添加文本。然后,我创建一个新线程从 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();
英文:

So I figured it out on my own. When creating the grid with textfields, I also add each textfield to an ArrayList tfs so that I can access each field individually to add the text later. I then create a new thread to add the text to each field from numsDrawn like so:

new Thread(() -&gt; {
			for (int i = 0; i &lt; 20; ++i) {
				final int j = i;
				Platform.runLater(() -&gt; tfs.get(j).setText(Integer.toString(numsDrawn.get(j))));
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}).start();

huangapple
  • 本文由 发表于 2020年10月5日 05:03:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/64199875.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定