【问题标题】:JavaFx set Label text from another controllerJavaFx 从另一个控制器设置标签文本
【发布时间】:2020-10-05 18:38:10
【问题描述】:

我想更改标签的文本,而不是从我声明控制器的类更改为 fxml 文件。

我想在主文件的 ClientAccept 类中更改它,但是当我尝试从 ClientAccept 获取 Controller 类并在 Controller 类中运行函数时,我得到 NullPointerException '因为“this.sStatus”为空'。我解决不了。

Main.java:

package sample;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.stage.Stage;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

public class Main extends Application {

ServerSocket ss;
HashMap clientColl = new HashMap();

@Override
public void start(Stage stage) throws Exception{

    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Controller controller = new Controller();
    loader.setController(controller);
    Parent parent = loader.load();
    Scene scene  = new Scene(parent);
    stage.setScene(scene);
    stage.show();

    try{
        ss = new ServerSocket(8989);
    }catch (IOException ioe){
        System.out.println(ioe.getMessage());
    }

    ClientAccept cla = new ClientAccept();
    cla.isWorking();

}


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


class ClientAccept extends Thread{

    public void isWorking() throws IOException {
        Controller controller = new Controller();
        controller.setsStatus("test");
    }

    public void run(){
        while(true){
            try {
                Socket s = ss.accept();
                String i = new DataInputStream(s.getInputStream()).readUTF();
                if(clientColl.containsKey(i)){
                    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                    dos.writeUTF("Już jesteś Zarejestrowany");
                }else {
                    clientColl.put(i, s);
                    Platform.runLater(()->{
                        controller.setsStatus("TextToLabel");
                    });
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

}

Controller.java:

package sample;


import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;

public class Controller {
@FXML
Label sStatus;
@FXML
TextArea msgBox;

void setsStatus(String text){
    sStatus.setText(text);
    System.out.println(text);
}

}

sample.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>


    <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"     prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <TextArea fx:id="msgBox" layoutX="53.0" layoutY="67.0" prefHeight="278.0" prefWidth="512.0" />
      <Label layoutX="67.0" layoutY="28.0" text="Server Status: " />
      <Label fx:id="sStatus" layoutX="142.0" layoutY="28.0" text=".............." />
   </children>
</AnchorPane>

我刚刚得到 NullPointerException。

【问题讨论】:

  • 您需要在传递给FXMLLoader 的同一个Controller 实例上调用该方法,而不是在新实例上。
  • 但是我需要像我之前粘贴的那样做。有什么方法可以做到吗?
  • 对不起,我不明白那个评论。正如我之前所说,只需调用您传递给FXMLLoader 的控制器实例上的方法。显然标签不会在你创建的新实例中初始化。

标签: java javafx


【解决方案1】:

@FXML-annotated 字段仅在 FXMLLoader 使用的 Controller 实例中初始化。它们不会在其他实例中被初始化,例如您在 ClientAccept 类中创建的实例。

一种快速而简单的方法是安排ClientAccept 引用正确的Controller 实例。例如

public void start(Stage stage) throws Exception{

    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Controller controller = new Controller();
    loader.setController(controller);
    Parent parent = loader.load();
    Scene scene  = new Scene(parent);
    stage.setScene(scene);
    stage.show();

    try{
        ss = new ServerSocket(8989);
    }catch (IOException ioe){
        System.out.println(ioe.getMessage());
    }

    ClientAccept cla = new ClientAccept(controller);
    cla.isWorking();

}


class ClientAccept extends Thread{

    private final Controller controller ;

    public ClientAccept(Controller controller) {
        this.controller = controller ;
    }

    public void isWorking() throws IOException {
        controller.setsStatus("test");
    }

    public void run(){
        while(true){
            try {
                Socket s = ss.accept();
                String i = new DataInputStream(s.getInputStream()).readUTF();
                if(clientColl.containsKey(i)){
                    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                    dos.writeUTF("Już jesteś Zarejestrowany");
                }else {
                    clientColl.put(i, s);
                    Platform.runLater(()->{
                        controller.setsStatus("TextToLabel");
                    });
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

像这样共享控制器实例通常不是很好的做法。更好的方法是使用 MVC 设计并共享模型。例如。你可以这样做

public class DataModel {

    private final StringProperty status = new SimpleStringProperty();

    public StringProperty statusProperty() {
        return status ;
    }

    public final String getStatus() {
        return statusProperty().get();
    }

    public final void setStatus(String status) {
        statusProperty().set(status);
    }

    // Similarly for other data shared by the app....

}

然后

public class Controller {
    @FXML
    Label sStatus;
    @FXML
    TextArea msgBox;

    private final DataModel model ;

    public Controller(DataModel model) {
        this.model = model ;
    }

    @FXML
    public void initialize() {
        sStatus.textProperty().bind(model.statusProperty());
    }


}

class ClientAccept extends Thread{

    private final DataModel model ;

    public ClientAccept(DataModel model) {
        this.model = model ;
    }

    public void isWorking() throws IOException {
        model.setStatus("test");
    }

    public void run(){
        while(true){
            try {
                Socket s = ss.accept();
                String i = new DataInputStream(s.getInputStream()).readUTF();
                if(clientColl.containsKey(i)){
                    DataOutputStream dos = new DataOutputStream(s.getOutputStream());
                    dos.writeUTF("Już jesteś Zarejestrowany");
                }else {
                    clientColl.put(i, s);
                    Platform.runLater(()->{
                        model.setStatus("TextToLabel");
                    });
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}

然后把它们绑在一起:

public void start(Stage stage) throws Exception{

    DataModel model = new DataModel();

    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Controller controller = new Controller(model);
    loader.setController(controller);
    Parent parent = loader.load();
    Scene scene  = new Scene(parent);
    stage.setScene(scene);
    stage.show();

    try{
        ss = new ServerSocket(8989);
    }catch (IOException ioe){
        System.out.println(ioe.getMessage());
    }

    ClientAccept cla = new ClientAccept(model);
    cla.isWorking();

}

【讨论】:

  • 这非常适合在另一个构造函数的标签上设置文本。但是我将如何显示/隐藏它的可见性?我应该通过构造函数还是通过模型传递对文本标签的引用?
  • @DIRTYDAVE “我应该传递对标签的引用” 绝对不是。 UI 元素本身不应该被共享。只需在模型中创建一个属性,该属性表示应用程序的 状态,它决定是否应显示标签。在控制器中注册一个监听器,并使标签可见或不存在。然后,您可以从引用该模型的任何其他位置更改该状态。
  • 注册监听器工作得很好。感谢您的帮助!
猜你喜欢
  • 2016-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多