-2

我有一个功能可以在 JavaFX GUI 中更改图像及其不透明度:

private static Image image = null;
private static ImageView imageView = new ImageView();
   
// some code to add image in GUI

public static void changeImage() {
    imageView.setOpacity(0.5);
    imageView.setImage(null);
}

当我在 JavaFX 实例中调用此函数时,如果我使用图像而不是nullsetImage(),图像会消失或正在发生变化。我尝试通过按下按钮来调用该函数。

在这种情况下,一切都按我的预期工作。

当我从另一个类调用这个函数时,实际的图像会改变它的不透明度,但图像本身永远不会改变。我通过以下方式调用该函数:

public static void changeImg() {
    Platform.runLater(() -> FX_Gui.changeImage());
}

更改标签,进度条......一切正常,但我没有设法更改图像。

4

2 回答 2

0

这个问题有很多方面没有意义。

一般来说,JavaFX 中的 GUI 旨在实现自包含和非线性执行。对外部方法进行编程以假设GUI 的某些状态,然后根据该假设直接操作 GUI,这不是正确的方法。因此,任何通过调用调用来了解GUI 状态的尝试Thread.sleep()本质上都是不正确的。

new JFXPanel()不需要调用,因为将Application.launch()初始化 JavaFX。据推测,这是在sleep(500)放入之前添加的,因为如果在命令changeImg()之后立即运行,调用将会失败,因为甚至还没有时间启动。Thread.start()launch()

如前所述,一旦屏幕完成初始化,就应该在方法中完成某种启动图像的替换FX_Min.start(Stage),尽管您甚至不太可能看到第一张图像。

这个问题似乎旨在设计一种应用程序,其中 GUI 只是它的一小部分,主应用程序将继续进行冗长的处理,然后触发 GUI 响应该处理的结果. 或者,主应用程序可能正在监视外部 API 并定期向 GUI 提供更新。然而,在大多数情况下,通常会初始化 GUI,以便它可以控制操作、启动后台线程以进行冗长的处理并使用 JavaFX 工具来处理触发 GUI 更新和获取结果。

在设计确实需要除 GUI 之外的其他东西作为中央控制的情况下,使用Application似乎不合适。毕竟,它旨在控制应用程序,并在 GUI 启动后监控 GUI 的状态,以便在 GUI 关闭时关闭所有内容。这就是为什么 OP 必须将Application.launch()调用放在一个单独的线程中 -launch()在 GUI 关闭之前不会返回。

如果 GUI 之外的应用程序要控制一切,那么最好使用 手动启动 JavaFX Platform.startup(),并手动处理所有监视。以下代码不进行任何监控,但它确实启动了 GUI 并更改了图像而没有任何问题:

public class Control_Min {

    public static void main(String[] args) {
        Platform.startup(() -> new Fx_Min().start(new Stage()));
        Platform.runLater(() -> Fx_Min.changeImage());
    }
}

请注意,不需要对Fx_Min. 但是,没有理由Fx_Min再扩展Application了,其start()方法中的代码可以放在任何地方。

应该进一步指出,尽管这可行,但它确实超出了 JavaFX 应用程序的规范。OP 的情况可能确实需要这种架构,但这会将其置于极少数应用程序中。通过提供的 JavaFX 工具围绕后台线程设计应用程序Application.launch()并在后台线程中启动冗长的处理几乎总是一种更好的方法。


好的,所以鉴于来自 OP 的新信息,很明显这应该基于Application并且 GUI 应该启动某种可能会阻止等待输入的套接字侦听器。

任何阻塞都不能在 FXAT 上运行,并且需要有一种方法允许套接字侦听器在接收到数据时与 GUI 进行通信。理想情况下,套接字侦听器应该是 JavaFX 不知道的,并且只是普通的 Java。

IMO,最好的方法是提供一个Consumer接受来自套接字侦听器的信息,并将其传递给它的构造函数中的套接字侦听器。这样,GUI 对套接字侦听器的性质一无所知,只是它依赖于需要消息使用者。类似地,套接字侦听器不知道是什么调用了它,只是它给了它一个消息消费者。

这限制了您的耦合,您可以自由地编写您的 GUI,而不必担心套接字侦听器的任何内部工作,反之亦然。

所以这里是 GUI,清理并简化了一点,以便更容易理解套接字侦听器的内容。基本上,GUI 只是将来自套接字侦听器的消息扔到Text屏幕上。消息消费者处理,Platform.runLater()以便套接字侦听器甚至不知道它:

public class Fx_Min extends Application {

    @Override
    public void start(Stage primaryStage) {
        ImageView imageView = new ImageView(new Image("/images/ArrowUp.png"));
        Text text = new Text("");
        primaryStage.setScene(new Scene(new VBox(10, imageView, text), 800, 600));
        primaryStage.setResizable(true);
        primaryStage.show();
        imageView.setImage(new Image("/images/Flag.png"));
        new SocketListener(socketMessage -> Platform.runLater(() -> text.setText(socketMessage))).startListening();
    }

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

这是套接字侦听器。显然,这不会在套接字上侦听,但它会围绕 a 循环sleep()以模拟 Pi 上发生的动作。这里的消息格式是String,只是为了让一切简单,但显然这是实际实现的更糟糕的选择。构建一个特殊的消息类:

public class SocketListener {

    private Consumer<String> messageConsumer;

    public SocketListener(Consumer<String> messageConsumer) {
        this.messageConsumer = messageConsumer;
    }

    public void startListening() {
        Thread listenerThread = new Thread(() -> listenForIRCommand());
        listenerThread.setDaemon(true);
        listenerThread.start();
    }

    private void listenForIRCommand() {
        for (int x = 0; x < 100; x++) {
            try {
                Thread.sleep(5000);
                messageConsumer.accept("Station " + x);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

应该非常清楚的是,由于调用listenForIRCommand()是从后台线程内部执行的,因此它完全摆脱了任何 JavaFX 约束。在 Java 中通常可以做的任何事情都可以从那里完成,而不必担心它对 GUI 的影响。

于 2021-06-14T14:14:58.987 回答
-1

同时我发现不改变图像的原因是我在GUI初始化完成之前运行了changeImage()。如果我在发送 changeImage() 命令之前等待大约 500 毫秒,一切正常。

下面是演示我遇到的问题的最小代码:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;

public class Control_Min {

    public static void changeImg() {
        Platform.runLater(() -> Fx_Min.changeImage());
    }
    
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                Application.launch(Fx_Min.class);
            }
        }.start();
        // JFXPanel will initialize the JavaFX toolkit. 
        new JFXPanel();  
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        changeImg();        
    }
}

还有 Gui 本身:

public class Fx_Min extends Application {
    private static Stage stage;
    private static GridPane rootPane;
    private static Scene scene;
    private static Image image = null;  
    private static ImageView imageView = new ImageView();
    
    @Override
    public void start(Stage primaryStage) {
        stage = primaryStage;
        rootPane = new GridPane();
        scene = new Scene(rootPane,800,600);            
        try {
            image = new Image(new FileInputStream("C:\\Users\\Peter\\eclipse-workspace\\FX_Test\\src\\application\\Image1.jpg"));
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }
        imageView.setImage(image);
        rootPane.add(imageView, 1, 0);  
        stage.setScene(scene);
        stage.setResizable(true);
        stage.show();   
        System.out.println("Gui is ready");
    }
    
    public static void changeImage() {
        try {
            image = new Image(new FileInputStream("C:\\Users\\Peter\\eclipse-workspace\\FX_Test\\src\\application\\Image2.jpg"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        imageView.setImage(image);
        System.out.println("Image Changed");
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

这段代码工作正常。在控制台中我得到:
Gui is ready
Image Changed

当我删除 Thread.sleep(500) 时,图像不会改变。在控制台中我得到:
Image Change
Gui is ready
我的结论是我已经在 FX 运行时初始化之前发送了 runlater 方法。
(还没有解决静态问题,因为这不是问题。我稍后会在我的原始程序中做。)

我的任务如下:
我在我的 PC 上为我的网络广播播放器编写一个 GUI。GUI 控制收音机并轮询正在播放的内容。我也想用红外遥控器控制收音机。我已经有一个与遥控器通信的树莓派。
因此,我的计划是在 PC 上运行一个服务器套接字,它接收来自树莓派的命令。服务器将在自己的线程中运行。我想使用 runLater 命令来更新 GUI。有没有更好的方法从服务器更新 GUI?目标是当我按下遥控器上的按钮时,GUI 将立即更新。
随着我对 JavaFX 的最新了解,我现在将直接在 FX 类中启动应用程序,并从 FX 类启动服务器线程

于 2021-06-11T15:03:13.347 回答