User interface 如何使用JavaFX中的后台工作在GUI中进行更改?

User interface 如何使用JavaFX中的后台工作在GUI中进行更改?,user-interface,javafx,task,runnable,User Interface,Javafx,Task,Runnable,从所有的搜索和阅读中可以清楚地看到,我需要调用Platform.runLater()来更改GUI。似乎我还需要使用可运行的界面。也许我也应该使用任务 但我不知道该怎么使用它们。另外,我不确定该把它们放在哪一类。我是JavaFX的新手 我的试用版JavaFX项目只有一个标签和一个文本字段。标签包含一个问题,文本字段用于回答。很简单 我在这里遇到了问题: 答案检查方法在一个单独的类中。我不知道如何访问GUI/FXML的组件并对其进行更改。其他类中的方法是静态的,而GUI/FXML的组件是非静态的 因

从所有的搜索和阅读中可以清楚地看到,我需要调用Platform.runLater()来更改GUI。似乎我还需要使用可运行的界面。也许我也应该使用任务

但我不知道该怎么使用它们。另外,我不确定该把它们放在哪一类。我是JavaFX的新手

我的试用版JavaFX项目只有一个标签和一个文本字段。标签包含一个问题,文本字段用于回答。很简单

我在这里遇到了问题:

答案检查方法在一个单独的类中。我不知道如何访问GUI/FXML的组件并对其进行更改。其他类中的方法是静态的,而GUI/FXML的组件是非静态的

因为我的实际项目会有很多测验,所以我热衷于使用单独的课程来检查答案

这里只有3个小班是相关的:

  • 包含main方法的“Launcher”类
  • FXML文件的“ViewController”类以及一些方法
  • “Ans”类,它有一个检查答案输入的方法
  • 我应该将Platform.runLater()放在哪个类中?代码是怎样的

    我将分享“Ans”和“ViewController”类的代码

    Ans(背景工作应该发生在这个文件中。在注释中,我提到了我想做但不能做的事情。例如,我想从那里设置标签文本,但我不能。因为我不知道如何做,我只是在那里放了一个System.out.Println。在它旁边的注释中,我提到了我真正想做的事情。)

    视图控制器

    package com.dan.view;
    
    import java.io.IOException;
    import java.net.URL;
    import java.util.ResourceBundle;
    
    import com.dan.ans.Ans;
    import com.dan.qn.Qn;
    
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextField;
    
    public class ViewController implements Initializable {
    
    private static String textFieldInput;      // I don't know how to access the typed info in the textField from another class. So I store it here and get it from it.
    
    // This is the getter I use for it. (See above)
    public static String getTextFieldInput() {
        return textFieldInput;
    }
    
    @FXML
    private Label label;
    
    @FXML
    private TextField textField;
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
    
        Qn.setQuestion();                       // This method is in the Qn class. It retrieves data from the db file and keeps them in variables.
    
        label.setText(Qn.getQn());              // This sets the label's text using the retrieved data. So you see the first question when the program opens.
    }
    
    // Event Listener on TextField[#textField].onAction
    public void enter(ActionEvent event) throws IOException {
    
        textFieldInput = textField.getText();    // Stores the typed info in the variable to be accessed from elsewhere.
    
        Ans.checkAns();                         // Runs the checkAns to check if the typed answer is correct or not.
    
      }
    
    }
    
    “Launcher”方法看起来就像任何带有主类的方法,所以我没有在这里分享它的代码

    有人能告诉我如何从其他类(如“Ans”)更新GUI中的组件吗?我很确定我应该使用Platform.runLater()和Runnable。也可能是Tasks。我看过几个例子,但不清楚如何在这个上下文中使用它


    非常感谢!:)

    这里的问题不是特别清楚。自然的(无论如何,对我来说)方法就是将
    checkAnswer(…)
    方法简单地变成一个“按框上的说明做”的方法,即将答案作为参数,检查它,并向调用方返回一个值,指示它是否正确

    这样,您还可以避免所有丑陋的
    静态
    黑客攻击

    public class Ans {
    
        public boolean checkAns(String answer) {
            // not really sure what Qn is here, but you can also clean this up and
            // get rid of the static methods
            if (answer.equalsIgnoreCase(Qn.getAns()) {
                // not sure if this really belongs here?
                Qn.setQuestion(); // really takes no parameters? Sets it to what, then?
                return true ;
            } else {
                return false ;
            }
        }
    }
    
    然后在你的控制器里,你可以

    public class ViewController implements Initializable {
    
        private Ans ans ;
    
        @FXML
        private Label label;
    
        @FXML
        private TextField textField;
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
    
            ans = new Ans();
            // ...
        }
    
        // ...
    
        public void enter(ActionEvent event) {
            if (ans.checkAns(textField.getText())) {
                // update UI to show answer was correct, etc
            } else {
                // update UI to show answer was incorrect...
            }
        }
    
        // ...
    }
    
    请注意,这如何允许您保持适当的关注点分离:
    Ans
    类根本不需要知道关于UI的任何信息(它根本不应该知道),并且所有特定于UI的代码都封装在它所属的控制器类中


    现在还不清楚您为什么询问
    Platform.runLater(…)
    并使用
    Task
    ,因为您发布的代码似乎都不涉及任何后台线程(也就是说,这些代码似乎都没有花费相当长的运行时间)。例如,如果
    checkAns(…)
    方法正在执行一些远程查找,并且确实需要时间运行,那么您将在
    任务中执行它,并从任务的
    onSucceeded
    处理程序更新UI。参见,例如。你的问题似乎更多的是关于基本的OO设计以及如何定义不同对象之间的关系;我认为您实际上根本没有询问线程问题。

    本例中的背景工作在哪里?示例代码似乎根本不需要单独的线程。很抱歉没有说清楚。这是“Ans”类中的代码(第一个代码示例)。在评论中,我提到了我想做但不能做的事情。就像我想从那里设置标签文本,但我不能。因为我不知道怎么做,所以我就在那里放了一个系统。在它旁边的评论中,我提到了我真正想要做的事情。但是这些和线程有什么关系呢?您在任何地方都没有真正解释过这一点。不过,更一般地说,您应该使用一个模型类,并从控制器(或多个控制器)观察和更新它。请参见示例Still:“我想在GUI标签上放置另一个类的字符串”。这与线程有什么关系?为什么您要询问
    平台.runLater()
    ?静态或非静态与任何事情有什么关系?如果要调用非静态方法,需要将控制器实例的引用传递给
    Ans
    类(尽管我也不会使该类中的方法成为静态的),非常感谢。你的解决方案很有意义。我决定把“Ans”中的所有代码都插入控制器。只需要多加几行。另外,感谢您解释Platform.runLater()的用途。现在它有意义了。我理解我的问题不够清楚,我真的很感谢你抽出时间回答我。:)
    public class ViewController implements Initializable {
    
        private Ans ans ;
    
        @FXML
        private Label label;
    
        @FXML
        private TextField textField;
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
    
            ans = new Ans();
            // ...
        }
    
        // ...
    
        public void enter(ActionEvent event) {
            if (ans.checkAns(textField.getText())) {
                // update UI to show answer was correct, etc
            } else {
                // update UI to show answer was incorrect...
            }
        }
    
        // ...
    }