Image 当Platform.runLater调用函数时,JavaFX映像未更改

Image 当Platform.runLater调用函数时,JavaFX映像未更改,image,javafx,Image,Javafx,我有一个在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(nul

我有一个在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实例中调用此函数时,如果我使用图像而不是setImage()的
null
,则图像将消失或正在更改。我试着按下一个按钮来调用这个函数

在这种情况下,一切都如我所料

当我从另一个类调用此函数时,实际图像将更改其不透明度,但图像本身永远不会更改。我按以下方式调用该函数:

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

正在更改标签、进度条。。。所有这些都可以工作,但我没有设法更改图像。

与此同时,我发现没有更改图像的原因是我在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已准备就绪
形象改变

当我移除线程时。sleep(500)图像不会改变。 在控制台中,我得到:
图像更改
Gui已准备就绪
我的结论是,我已经在FX运行时初始化之前发送了runlater方法。
(尚未修复静态问题,因为这不是问题所在。我将稍后在原始程序中进行修复。)

我的任务如下:
我在我的电脑上为我的互联网收音机播放器编程一个GUI。 GUI控制收音机并轮询正在播放的内容。 我也想用红外线遥控器控制收音机。 我已经有了一个与遥控器通信的树莓Pi。
因此,我的计划是在PC上运行一个服务器套接字,它接收来自Raspberry Pi的命令。 服务器将在其自己的线程中运行。我想使用runLater命令来更新GUI。 是否有更好的方法从服务器更新GUI? 目标是当我按下遥控器上的按钮时,GUI将立即更新。

根据我对JavaFX的最新了解,我现在将直接在FX类中启动应用程序,并从FX类启动服务器线程

一般来说,JavaFX中的GUI在执行时是自包含和非线性的。将外部方法编程为假定GUI的某个状态,然后根据该假定直接操作GUI不是正确的方法。因此,任何试图通过在
Thread.sleep()
调用中偷懒来了解GUI状态的尝试本质上都是不正确的

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

如前所述,在屏幕完成初始化后,应在
FX_Min.start(Stage)
方法中更换某种启动映像,尽管您甚至不太可能看到第一个映像

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

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

如果GUI之外的应用程序要控制一切,那么最好使用
Platform.startup()
手动启动JavaFX,并手动处理所有监控。以下代码不进行任何监视,但它会启动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\u Min
中的OP代码无需更改。但是,
Fx_Min
没有理由再扩展
应用程序
,它的
start()
方法中的代码可以放在任何地方

应该进一步注意的是,尽管这是可行的,但它确实远远超出了JavaFX应用程序的标准。OP的情况可能确实需要这种体系结构,但这将使它成为一小部分应用程序。围绕
application.launch()
设计应用程序,并通过提供的JavaFX工具在后台线程中启动冗长的处理,几乎总是一种更好的方法


好的,如果OP提供了新的信息,很明显这应该基于
应用程序
,GUI应该启动某种套接字侦听器,可能会阻止等待输入

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

在国际海事组织,最好的办法是提供<
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);
    }
}
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();
            }
        }
    }
}