【问题标题】:JavaFX show object at runtimeJavaFX 在运行时显示对象
【发布时间】:2021-08-27 13:12:28
【问题描述】:

我有一个 javafx 应用程序,我需要在运行时显示/编辑/操作一些对象。 例如我有一个anchorPane,当我单击一个按钮时,我需要将窗格设置为可见,并且在执行逻辑期间,将一些标签设置为可见。

这是我的代码:


//MyController.java
public class MyController extends AnchorPane {
    @FXML
    private AnchorPane myPane;
    @FXML
    private Label label1;
    @FXML
    private Label label2;
    @FXML
    private Button button1;

    private Main application;
    public void setApp(Main application) {
        this.application = application;
    }

    public void initialize()
    {
        myButton.setOnAction(new EventHandler<ActionEvent>()
        {
            @override
            public void handle(ActionEvent arg0)
            {
                myPane.setVisile(true);.
                //long run action
                label1.setVisible(true);
                //long run action
                label2.setVisibile(true);
                 //long run action
                button1.setText("myText");
                 //long run action
                button1.setVisibile(true);
                //end of operations
            }
        )};
    }
}

我不明白为什么 GUI 冻结和所有元素在操作结束时一起显示,我试图查看有关 javafx 线程管理的一些内容,但我无法实现令人满意的解决方案.. 有人可以帮助我吗?

谢谢大家。

【问题讨论】:

  • 您正在应用程序线程上进行更改,该线程在应用程序运行时暂停应用程序更新(冻结应用程序)尝试将您的更改包装在Platform.runLater(()-&gt; ... )
  • 我试过这个解决方案,不幸的是不起作用。
  • 你能不能发布一个完整的可运行的minimal reproducible example 然后去掉代码,这样它就没有任何我们不需要重现问题的额外代码
  • 您不能在事件处理程序中执行长时间运行的操作。在您的处理程序返回之前,UI 无法重绘自身(或响应用户输入)。这就是它结冰的原因。您需要在不同的线程中执行长时间运行的操作,然后像 Matt 建议的那样使用 Platform.runLater,或者在由 Service 创建的任务中执行这些操作。

标签: java multithreading user-interface javafx interface


【解决方案1】:

这是一个利用 java.util.concurrentjavafx.concurrent API 的解决方案:

Executor exec = Executors.newSingleThreadedExecutor();

// ...

button.setOnAction(event -> {
    myPane.setVisible(true);
    Task<Void> task1 = createTask(this::doTask1, () -> label1.setVisible(true));
    Task<Void> task2 = createTask(this::doTask2, () -> label2.setVisible(true));
    Task<Void> task3 = createTask(this::doTask3, () -> button1.setText("myText"));
    Task<Void> task4 = createTask(this::doTask4, () -> button1.setVisible(true));
    List.of(task1, task2, task3, task4).forEach(exec::execute);
});

// ...

private Task<Void> createTask(Runnable longRunningProcess, Runnable onSucceeded) {
    Task<Void> task = new Task<>() {
        @Override
        public Void call() {
            longRunningProcess.run();
            return null ;
        }
    };
    task.setOnSucceeded(e -> onSucceeded.run());
    return task ;
}

private void doTask1() {
    // long running operation...
}
private void doTask2() {
    // long running operation...
}
private void doTask3() {
    // long running operation...
}
private void doTask4() {
    // long running operation...
}

【讨论】:

  • 不知道任务真的需要连续运行吗?大多数刚接触 JavaFX 的程序员似乎都在线性考虑所有事情——先做这个,然后做那个,等待答案,然后再做其他事情——这通常不是在事件驱动的环境中做这件事的正确方法。
  • @DaveB 我希望答案与问题一致,其中四个任务清楚地以线性方式执行。这种方法的一个优点是并行运行它们的更改只是对单行代码的更改(如果需要,甚至可以使用 DI 进行外部化)。
【解决方案2】:

这是一个可快速运行的示例,用于编写我的评论一个简单的演示,但建议使用TaskService。请注意,您仍然可以单击RadioButtons

,因此您可以看到主线程并未冻结
public class Main extends Application {
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox vBox = new VBox();
        vBox.setPrefSize(200, 200);
        vBox.setAlignment(Pos.CENTER);


        vBox.getChildren().add(new Label("Main APP"));

        Pane pane = new Pane(new Label("THIS IS INSIDE THE PANE"));
        vBox.getChildren().add(pane);

        Button button = new Button("Activate Pane Cloaking");
        button.setOnAction(event -> {
            new Thread(() -> {
                simulateCodeRunning();//5 sec
                Platform.runLater(() -> pane.setVisible(!pane.isVisible()));

                if (pane.isVisible())
                    Platform.runLater(() ->button.setText("Deactivate Cloaking"));
                else
                    Platform.runLater(() ->button.setText("Activate Pane Cloaking"));
            }).start();
        });
        vBox.getChildren().add(button);

        ToggleGroup radioGroup = new ToggleGroup();
        RadioButton radioButton1 = new RadioButton("Click Me");
        radioButton1.setToggleGroup(radioGroup);
        RadioButton radioButton2 = new RadioButton("Click Me");
        radioButton2.setToggleGroup(radioGroup);
        vBox.getChildren().addAll(radioButton1, radioButton2);


        primaryStage = new Stage();
        primaryStage.setScene(new Scene(vBox));
        primaryStage.show();

    }

    private void simulateCodeRunning() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多