如何从外部更新 JavaFX 场景?

2023-12-05

我正在尝试学习 JavaFX 并将 swing 应用程序转换为 JavaFX。 我想做的是使用JavaFX来显示程序的进度。

我之前在 Swing 中所做的事情是首先使用自定义 JComponent 创建一个 JFrame。然后让我的主程序调用自定义 JComponent 的方法,该方法将更改 JComponent 和 repaint() 内形状的颜色。

下面给出了我想在 JavaFX 中实现的目标:

//Run JavaFX in a new thread and continue with the main program.
public class Test_Main{
    public static void main(String[] args) {
        Test test = new Test();
        Thread t = new Thread(test);
        t.start();

        //Main Program
        JOptionPane.showMessageDialog(null, "Click 'OK' to continue.",
                "Pausing", JOptionPane.INFORMATION_MESSAGE);

        //Update Progress
        test.setText("Hello World!");
    }    
}

我目前将其作为我的可运行程序。

public class Test extends Application implements Runnable{
    Button btn;

    @Override
    public void run() {
        launch();
    }

    @Override
    public void start(Stage stage) throws Exception {
        StackPane stack = new StackPane();
        btn = new Button();
        btn.setText("Testing");
        stack.getChildren().add(btn);
        Scene scene = new Scene(stack, 300, 250);
        stage.setTitle("Welcome to JavaFX!");
        stage.setScene(scene);
        stage.show();        
    }    

    public void setText(String newText){
        btn.setText(newText);
    }
}

一切都运行良好,直到我尝试更新按钮的文本,其中我得到了NullPointerException。我猜这与 JavaFX 应用程序线程有关。我在网上找不到任何描述如何从外部更新内容的内容。

我看到很多提到Platform.runLater and Task但它们通常嵌套在 start 方法中并在计时器上运行。

UPDATE:只是为了澄清我希望实现这样的目标:

public class Test_Main{
    public static void main(String[] args) {
        final boolean displayProgress = Boolean.parseBoolean(args[0]);

        Test test = null;
        if(displayProgress){    //only create JavaFX application if necessary
            test = new Test();
            Thread t = new Thread(test);
            t.start();
        }

        //main program starts here

        // ...

        //main program occasionally updates JavaFX display
        if(displayProgress){    //only update JavaFX if created
            test.setText("Hello World!");
        }

        // ...

        //main program ends here
    }    
}

The NullPointerException与线程无关(尽管您的代码中也存在线程错误)。

Application.launch()是一个静态方法。它创建了一个实例Application子类,初始化 Java FX 系统,启动 FX 应用程序线程,并调用start(...)在它创建的实例上,在 FX 应用程序线程上执行它。

所以实例Test其上start(...)被调用的实例与您在中创建的实例不同main(...)方法。因此btn您创建的实例中的字段Test_Main.main()从未被初始化。

如果您添加一个仅执行一些简单日志记录的构造函数:

public Test() {
    Logger.getLogger("Test").log(Level.INFO, "Created Test instance");
}

您将看到创建了两个实例。

API 根本就不是为了以这种方式使用而设计的。你应该重视start(...)本质上作为替代品为了main当您使用 JavaFX 时的方法。 (事实上​​,在 Java 8 中,您可以省略main方法完全来自你的Application子类并仍然从命令行启动该类。)如果您希望一个类可重用,请不要将其作为Application;要么使其成为某些容器类型节点的子类,要么(在我看来更好)为其提供访问此类节点的方法。

您的代码中也存在线程问题,尽管这些问题不会导致空指针异常。作为场景图一部分的节点只能从 JavaFX 应用程序线程访问。 Swing 中也存在类似的规则:swing 组件只能从 AWT 事件处理线程访问,因此您确实应该调用JOptionPane.showMessageDialog(...)在那条线上。在JavaFX中,您可以使用Platform.runLater(...)安排一个Runnable在 FX 应用程序线程上运行。在 Swing 中,您可以使用SwingUtilities.invokeLater(...)安排一个Runnable在 AWT 事件调度线程上运行。

混合 Swing 和 JavaFX 是一个非常高级的主题,因为您必然需要在两个线程之间进行通信。如果您希望启动一个对话框作为 JavaFX 阶段的外部控件,那么最好将该对话框也设置为 JavaFX 窗口。

Updated:

根据评论中的讨论,我假设JOptionPane只是一种提供延迟的机制:我将在这里修改您的示例,以便它在更改按钮文本之前只需等待五秒钟。

最重要的是,您想要以不同方式重用的任何代码都不应该放在Application子类。创建一个Application子类仅作为启动机制。 (换句话说,Application子类确实不可重用;将除启动过程之外的所有内容放在其他地方。)因为您可能想要使用您调用的类Test不止一种方式,您应该将其放置在 POJO(普通的旧 Java 对象)中,并创建一个方法来访问它定义的 UI 部分(并挂钩任何逻辑;尽管在真实的应用程序中,您可能希望将逻辑考虑在内进入不同的班级):

import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;

public class Test {

    private Button btn;
    private Pane view ;

    public Test(String text) {
        Logger.getLogger("Test").log(Level.INFO, "Created Test instance");

        view = new StackPane();
        btn = new Button();
        btn.setText(text);
        view.getChildren().add(btn);

    }   

    public Parent getView() {
        return view ;
    }

    public void setText(String newText){
        btn.setText(newText);
    }
}

现在假设您想以两种方式运行。为了便于说明,我们将有一个TestApp以文本“Testing”启动按钮,然后五秒钟后将其更改为“Hello World!”:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApp extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // launch app:

        Test test = new Test("Testing");
        primaryStage.setScene(new Scene(test.getView(), 300, 250));
        primaryStage.show();

        // update text in 5 seconds:

        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException exc) {
                throw new Error("Unexpected interruption", exc);
            }
            Platform.runLater(() -> test.setText("Hello World!"));
        });
        thread.setDaemon(true);
        thread.start();

    }    
}

Now a ProductionApp立即启动它,文本直接初始化为“Hello World!”:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class ProductionApp extends Application {
    @Override
    public void start(Stage primaryStage) {
        Test test = new Test("Hello World!");
        primaryStage.setScene(new Scene(test.getView(), 300, 250));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

请注意,有一个重载形式Application.launch(...)这需要Application子类作为参数。所以你可以在其他地方有一个主要方法来决定哪个Application本来要执行:

import javafx.application.Application;

public class Launcher {

    public static void main(String[] args) {
        if (args.length == 1 && args[0].equalsIgnoreCase("test")) {
            Application.launch(TestApp.class, args) ;
        } else {
            Application.launch(ProductionApp.class, args);
        }
    }
}

请注意,您只能调用launch(...)每次调用 JVM 一次,这意味着最好只从main method.

继续“分而治之”主题,如果您希望选择“无头”运行应用程序(即根本没有 UI),那么您应该从 UI 代码中提取正在操作的数据。在任何实际规模的应用程序中,这都是一个很好的做法。如果您打算在 JavaFX 应用程序中使用数据,那么使用 JavaFX 属性来表示它会很有帮助。

在这个玩具示例中,唯一的数据是字符串,因此数据模型看起来非常简单:

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class DataModel {
    private final StringProperty text = new SimpleStringProperty(this, "text", "");

    public final StringProperty textProperty() {
        return this.text;
    }

    public final java.lang.String getText() {
        return this.textProperty().get();
    }

    public final void setText(final java.lang.String text) {
        this.textProperty().set(text);
    }

    public DataModel(String text) {
        setText(text);
    }
}

修改后的Test封装可重用 UI 代码的类如下所示:

import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;

public class Test {

    private Pane view ;

    public Test(DataModel data) {
        Logger.getLogger("Test").log(Level.INFO, "Created Test instance");

        view = new StackPane();
        Button btn = new Button();
        btn.textProperty().bind(data.textProperty());
        view.getChildren().add(btn);

    }   

    public Parent getView() {
        return view ;
    }
}

基于 UI 的应用程序如下所示:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApp extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // launch app:
        DataModel data = new DataModel("Testing");
        Test test = new Test(data);
        primaryStage.setScene(new Scene(test.getView(), 300, 250));
        primaryStage.show();

        // update text in 5 seconds:

        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException exc) {
                throw new Error("Unexpected interruption", exc);
            }

            // Update text on FX Application Thread:
            Platform.runLater(() -> data.setText("Hello World!"));
        });
        thread.setDaemon(true);
        thread.start();

    }    
}

仅操作数据而不附加视图的应用程序如下所示:

public class HeadlessApp {

    public static void main(String[] args) {
        DataModel data = new DataModel("Testing");
        data.textProperty().addListener((obs, oldValue, newValue) -> 
            System.out.printf("Text changed from %s to %s %n", oldValue, newValue));
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException exc) {
                throw new Error("Unexpected Interruption", exc);
            }
            data.setText("Hello World!");
        });
        thread.start();
    }

}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从外部更新 JavaFX 场景? 的相关文章

  • 如何在 Openfire 中使用 smack

    你好 我计划开发一个可以连接到 gtalk facebook 等的聊天客户端 我决定将 smack API 与 openfire 一起使用 但我需要很少的指导来了解如何将它与 openfire 服务器一起使用 openfire 是否提供了基
  • Base36 编码字符串?

    我一直在网上查找 但找不到解决此问题的方法 在 Python Ruby 或 Java 中 如何对以下字符串进行 Base 36 编码 nOrG9Eh0uyeilM8Nnu5pTywj3935kW 5 Ruby 以 36 为基数 s unpa
  • (Java) App Engine 中的静态文件无法访问

    The 示例文档 http code google com appengine docs java gettingstarted staticfiles html表示您只需将文件放在 war 或子目录 中 并且应该可以从主机访问它们 只要它
  • 如何将jscrollpane添加到jframe?

    我有以下源代码 有人可以给我建议如何将 jscrollpane 添加到 jframe 上吗 我尝试了几次将其添加到 jframe 但没有任何进展 它甚至没有显示 public class Form3 JFrame jframe new JF
  • 埃拉托色尼筛法 - 实现返回一些非质数值?

    我用 Java 实现了埃拉托斯特尼筛法 通过伪代码 public static void sieveofEratosthenes int n boolean numArray numArray new boolean n for int i
  • 从 MS Access 中提取 OLE 对象(Word 文档)

    我有一个 Microsoft Access 数据库 其中包含一个包含 Microsoft Word 文档的 OLE 对象字段 我试图找到代码来检索保存在 OLE 对象中的文件 以便用户可以从我的 JavaFx 应用程序中的按钮下载它 但没有
  • 如何在java Spring Boot中实现通用服务类?

    我有许多具有重复代码的服务 我想知道如何实现通用服务 以便我的所有服务都可以扩展它 服务接口示例 重复代码 Service public interface IUserService List
  • 为什么Iterator接口没有add方法

    In IteratorSun 添加了remove 方法来删 除集合中最后访问的元素 为什么没有add方法来向集合中添加新元素 它可能对集合或迭代器产生什么样的副作用 好的 我们开始吧 设计常见问题解答中明确给出了答案 为什么不提供 Iter
  • 如何检测图像是否像素化

    之前有人在 SO 上提出过这样的问题 在Python中检测像素化图像 https stackoverflow com questions 12942365 detecting a pixelated image in python还有关于q
  • 如何通过注解用try-catch包装方法?

    如果应该在方法调用中忽略异常 则可以编写以下内容 public void addEntryIfPresent String key Dto dto try Map
  • org/codehaus/plexus/archiver/jar/JarArchiver(不支持的major.minor版本49.0)-Maven构建错误

    下午大家 我在尝试构建项目时收到上述错误 我很确定这与使用 Java 1 6 编译的 Maven 最新更新有关 而我们尝试构建的项目是 1 4 项目 在此之前的插件工作没有问题 因此我将以下内容添加到 POM xml 文件中以尝试强制使用现
  • 在 Java 中通过 XSLT 分解 XML

    我需要转换具有嵌套 分层 表单结构的大型 XML 文件
  • 如何从日期中删除毫秒、秒、分钟和小时[重复]

    这个问题在这里已经有答案了 我遇到了一个问题 我想比较两个日期 然而 我只想比较年 月 日 这就是我能想到的 private Date trim Date date Calendar calendar Calendar getInstanc
  • 如何通过 Android 按钮单击运行单独的应用程序

    我尝试在 Android 应用程序中添加两个按钮 以从单独的两个应用程序订单系统和库存系统中选择一个应用程序 如图所示 我已将这两个应用程序实现为两个单独的 Android 项目 当我尝试运行此应用程序时 它会出现直到正确选择窗口 但是当按
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • Java - 从 XML 文件读取注释

    我必须从 XML 文件中提取注释 我找不到使用 JDOM 或其他东西来让它们使用的方法 目前我使用 Regex 和 FileReader 但我不认为这是正确的方法 您可以使用 JDOM 之类的东西从 XML 文件中获取注释吗 或者它仅限于元
  • 如何处理 StaleElementReferenceException

    我正在为鼠标悬停工作 我想通过使用 for 循环单击每个链接来测试所有链接的工作条件 在我的程序中 迭代进行一次 而对于下一次迭代 它不起作用并显示 StaleElementReferenceException 如果需要 请修改代码 pub
  • 源值 1.5 的错误已过时,将在未来版本中删除

    我使用 scala maven plugin 来编译包含 scala 和 java 代码的项目 我已经将源和目标设置为1 7 但不知道为什么maven仍然使用1 5 这是我在 pom xml 中的插件
  • 检查应用程序是否在 Android Market 上可用

    给定 Android 应用程序 ID 包名称 如何以编程方式检查该应用程序是否在 Android Market 上可用 例如 com rovio angrybirds 可用 而 com random app ibuilt 不可用 我计划从
  • 即使调整大小,如何获得屏幕的精确中间位置

    好的 这个问题有两部分 当我做一个JFrame 并在其上画一些东西 即使我将宽度设置为 400 并使其在一个项目击中它时 当然 允许项目宽度 它会反弹回来 但由于某种原因 它总是偏离屏幕约 10 个像素 有没有办法解决这个问题 或者我只需要

随机推荐

  • django-tables2 linkColumn 外部 url

    我有 2 个模型属性 model name 和 model url 我需要创建一个 linkColumn 该列名称 model name 并链接到中指定的 url 型号 url 有可能实现这样的目标吗 thanks 您可以使用 Templa
  • 如何在本地主机上运行html文件?

    我有一个 HTML 文件 并在本地主机上运行它 但是 该文件包含使用网络摄像头的镜像 例如 我怎样才能运行这个 HTML 文件在本地主机上 在此示例中 当选中实时复选框时 网络摄像头将启动 您可以在以下位置运行您的文件http服务器 1 g
  • EF Core SaveChanges 是否根据数据注释进行验证

    我有一个带有数据注释的模型 我想知道如果数据注释失败 SaveChanges 方法是否可能失败 我期望 SaveChanges 抛出 Test2 超出 2 到 4 范围 的异常 相反 它保存到数据库中 例如 这是我的测试实体 public
  • 需要 iPv6 兼容性 - iOS 应用被苹果拒绝

    6 月 1 日之后 我向 itunes connect 提交了我的 ionic 应用程序 并收到了来自苹果的消息 应用程序在 IPv6 网络上进行审核 请确保您的应用支持 IPv6 网络 as IPv6兼容性 是必须的 有关支持 IPv6
  • Powershell调用Github API:ConvertFrom-Json管道之谜

    我正在使用 PowerShell 调用 GitHub API 结果是一个 JSON 数组 我使用 ConvertFrom Json cmdlet 将其转换为 PowerShell 对象 这给了我一个 PowerShell 对象数组 但是 当
  • 如何按类查找元素

    我在使用 Beautifulsoup 解析具有 class 属性的 HTML 元素时遇到问题 代码看起来像这样 soup BeautifulSoup sdata mydivs soup findAll div for div in mydi
  • 如何实时查看设备上的应用变化?

    每次我更改代码时 即使只有一行 我也必须运行命令 sudo ionic cordova prepare ios 我必须单击 Xcode 上的播放按钮才能将应用程序上传到 iPhone 上并查看操作中的更改 基本上每次我想测试应用程序时 我都
  • 在 Google AppEngine 上实施 REST 服务 (JSON) [关闭]

    Closed 这个问题是基于意见的 目前不接受答案 我正在尝试在 Google AppEngine 上实现 REST 服务 我更喜欢使用 GAE Java 和 JSON 在服务和客户端之间进行通信 每个人最喜欢执行此操作的库是什么 您更喜欢
  • Excel vba - 比较两个范围并查找不匹配项

    我有两张 Excel 工作表 其中一张包含用户列表 而另一个列表包含相同的数据 只是同一用户被列出了多次 现在 我需要某种方法将第二个列表与第一个列表进行比较 并删除包含第一个列表中未找到的用户的行 第一个列表如下所示 保罗 麦卡特尼 约翰
  • 如何使用 JavaScript 以跨浏览器的方式将 DOM 序列化为 XML 文本?

    我有一个 XML 对象 使用加载XMLHTTPRequest s responseXML 我已经修改了该对象 使用 jQuery 并希望将其作为文本存储在字符串中 Firefox 等显然有一种简单的方法可以做到这一点 var xmlStri
  • 当结果集在逻辑应用中只有一条记录时,Foreach 不支持

    我必须从 XML 读取数据并将其加载到 SQL 因此 在中间 我需要为逻辑应用中的每个任务使用的每个记录添加一些业务逻辑 但是 当 XML 中只有一条记录时 结果将被视为对象而不是数组 并且逻辑应用程序失败 这是我的 XML 的样子
  • 计算iPhone所有尺寸的长宽比

    我知道这种类型的问题已经被问过好几次了 但我对 iphone 比较陌生 所以对所有答案感到困惑 因为我的徽标是 61 57 我想根据 iphone 的大小进行更改 但是当我指定所有屏幕的长宽比时 它会给出自己的比例 但我很困惑这个比率是如何
  • Html.RenderAction 使用 Post 而不是 Get

    我的页面上有一个简单的表格 提交后 它会检查是否ModelState IsValid如果无效 则返回具有相同模型的视图 在同一页面上 我正在呈现一个包含另一个表单的操作 如下所示 Html RenderAction AccountNote
  • 使用 sizeWithFont:constrainedToSize:lineBreakMode 计算 UITextView 文本的高度似乎没有返回正确的结果

    我正在尝试计算受 UITextView 约束的文本的高度 但它似乎没有返回正确的结果 这是我的代码 void textViewDidChange UITextView aTextView CGSize textSize aTextView
  • 如何在 Outlook Web App (OWA)、Outlook 2016 Mac 和 Windows 中的 Outlook 加载项中显示垂直窗格

    我正在开发一个针对 OWA Outlook 2016 for Mac Windows 的 Outlook 插件 我的要求是显示加载项垂直位于右侧在阅读和撰写邮件时 对于撰写 它默认显示为所需的 但对于阅读 它是水平显示的 我想垂直显示 简而
  • 主干,而不是“this.el”包装

    我广泛使用模板 我喜欢用完全包含模板 我的意思是我想看到template对所有 DOM 元素进行编码 包括root一 像这样 但 Backbone 喜欢的是template像这样
  • 如何以编程方式清除或更新 Azure AD B2C MFA 的电话号码?

    我们正在使用此处找到的示例在 Azure AD B2C 上测试 MFA https github com azure ad b2c samples tree master policies mfa unknown devices 我们知道这
  • 如何通过javascript获取查询字符串?

    如何从 JavaScript 中的 URL 中提取查询字符串 谢谢你 您可以轻松构建字典风格的集合 function getQueryStrings var assoc var decode function s return decode
  • 将 QGraphicsScene 保存到 Svg 会更改缩放比例

    我需要保存我的项目QGraphicsScene到 svg 并能够将该 svg 加载回场景 我能做到 但每次将画布保存到 svg 时 加载时项目都会变大 并且重复保存和加载相同的 svg 会导致其增大 我找不到原因 我附上了示例代码 以及结果
  • 如何从外部更新 JavaFX 场景?

    我正在尝试学习 JavaFX 并将 swing 应用程序转换为 JavaFX 我想做的是使用JavaFX来显示程序的进度 我之前在 Swing 中所做的事情是首先使用自定义 JComponent 创建一个 JFrame 然后让我的主程序调用