英文:
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'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 < 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);
}
}
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'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'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'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) -> {
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);
}
}
答案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<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++;
}
答案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(() -> {
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();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论