如何正确继承 FXML 带注释的属性以使其在子类中可用?

问题描述 投票:0回答:1

问题

我正在尝试通过控制器区域网络实现消息传输,其中消息是通过使用 JavaFX 创建的 GUI 根据用户输入构建的。

我有一个

MainController
类,它链接到 Main.fxml。在
MainController
中,我定义了一个 FXML 带注释的 TextField 属性
in_desiredVelocity
,它正确链接到 Main.fxml 中的
fx:id

然后我定义了一个抽象类

canMessage
,它定义了必须通过网络发送的消息的主干。

现在类

PcToVcuMessage
实现了一种特定类型的canMessage。为了能够访问 FXML 属性(在
MainController
中定义),我决定抽象类
canMessage
扩展
MainController
,并且
PcToVcuMessage
扩展
canMessage

应用程序编译正确,但当我在 GUI 中输入时,会启动 TextField

in_desiredVelocity
a
NullPointerException

问题

尽管上面的FXML属性是由

PcToVcuMessage
继承的(它继承自抽象类
canMessage
并且这个扩展了
MainController
),我如何在这个类中使用它来实现我的目标?

主要

package canbusgui;

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

import java.io.IOException;

public class MainApplication extends Application {
    public static Stage stage = null;

    @Override
    public void start(Stage stage) throws IOException {
        
        stage.setOnCloseRequest(event -> {
           System.exit(0);
        });

        MainApplication.stage = stage;

        // Create a FXMLLoader to load the FXML file that defines the user interface
        FXMLLoader fxmlLoader = new FXMLLoader(MainApplication.class.getResource("MainView.fxml"));

        Scene scene = new Scene(fxmlLoader.load());

        stage.setTitle("CANbus GUI");
        stage.setScene(scene);
        stage.setResizable(false);
        stage.setMinHeight(768);
        stage.setMinWidth(1366);
        stage.show();
    }

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

主控制器:

package canbusgui;

import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;


public class MainViewController {
  @FXML
  protected TextField in_desiredVelocity;

  @FXML
  public void initialize (){

        in_desiredVelocity.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
                if (event.getCode() == KeyCode.ENTER) {
                    try {
                        sendMessage(new PcToVcuMessage("222"));
                    }
                    catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
        });
  }
  

  public void sendMessage(canMessage message) throws Exception {
        
        message.constructData();
       
        message.validateInputs();
        
        byte[] data = message.getData();

        // Send the data array to the CAN bus
        canBusController.sendCommand(HexFormat.fromHexDigits(message.getId()), data);
  }

}

canMessage.java(它包含一个抽象类

canMessage
并且
PcToVcuMessage
扩展了它):

package canbusgui;

import static java.lang.Integer.parseInt;

public abstract class canMessage extends MainViewController{

    // Declare common attributes
    protected String id;
    protected byte[] data;

    public canMessage(String id) {
        this.id = id;
        // Initialize an empty byte array for data
        this.data = new byte[8];
    }


    // Define an abstract method to construct the data array
    public abstract void constructData();

    // Define an abstract method to validate the inputs
    public abstract void validateInputs() throws Exception;

    // Define a getter method for the data array
    public byte[] getData() {
        return this.data;
    }

    public String getId() {
        return this.id;
    }

}


// Define a subclass for PC_to_VCUMessage message
class PcToVcuMessage extends canMessage {


    public PcToVcuMessage(String id) {
        // Call the superclass constructor
        super(id);
    }


    // Override the constructData method
    @Override
    public void constructData() {


        data[0] = (byte) 0;
        data[1] = (byte) 0;
        data[2] = (byte) 0;
        data[3] = (byte) parseInt(in_desiredVelocity.getText()); //HERE in_desiredVelocity is null and a NillPointerException is launched
        data[4] = (byte) 0;
        data[5] = (byte) 0;
        data[6] = (byte) 0;
        data[7] = (byte) 0;
    }

    public  void validateInputs() throws Exception{}


}

编辑

CAN 报文的格式如下:ID(hex), data0, data1, data2, data3, ......, data7。因此,当我在控制器中调用

PcuToVcuMessage
的构造函数时,我将传递消息 222 的 ID(顺便说一句,该 ID 在设备的数据表中指定)

PcuToVcuMessage
中,我需要访问用户通过在 GUI 的 TextField 中键入值来设置的 FXML 属性
in_desiredVelocity
:通过这种方式,可以按顺序检索用户键入的值构建消息。

编辑2

由于可以有多个不同ID的消息,所以我想到了在控制器中的sendMessage方法中使用多态性。此外,可能存在需要从控制器类访问多个 FXML 属性的消息。

java javafx javafx-8 fxml
1个回答
0
投票

这根本不是继承的作用。继承是之间的关系,而不是对象之间的关系。

当你这样做时

public class MainViewController {
    // ...
    protected TextField inDesiredVelocity;
    // ...
}

这意味着

MainViewController
的每个实例都会有一个名为
inDesiredVelocity
类型为
TextField
的字段。

当你这样做时

public abstract class CanMessage extends MainViewController {
    // ...
}

这意味着

CanMessage
的每个实例也是
MainViewController
的实例。

加载 FXML 时,

FXMLLoader
会创建
MainViewController
的实例(因为 FXML 中有
fx:controller="canbusgui.MainViewController"
),并使用对在该实例
中声明的文本字段的引用来初始化 
inDesiredVelocity 字段 FXML。

稍后在您的控制器中,您可以

new PcToVcuMessage("222")

当然,这会创建一个 id 为

PcToVcuMessage
的新实例
"222"
。由于
PcToVcuMessage
继承自
CanMessage
,因此该新实例也是
CanMessage
的实例,并且由于
CanMessage
继承自
MainViewController
,因此该实例也是
MainViewController
的实例,并且由于
 的每个实例MainViewController
有一个字段
inDesiredVelocity
PcToVcuMessage
的这个新实例有一个名为
inDesiredVelocity
类型为
TextField
的字段。

但是,您永远不会初始化该字段(并且没有明智的方法来这样做),因此

inDesiredVelocity
中的
PcToVcuMessage
字段为空。


这样做没有任何意义。我不太明白你的领域模型是什么(我可能不需要回答这个问题),但是对于

TextField
作为某种类型的对象的一部分来说没有任何意义消息。

相反,将此消息发送的数据作为

PcToVcuMessage
的一部分可能是有意义的。 IE。你可以做

class PcToVcuMessage extends CanMessage {

    private int desiredVelocity ;


    public PcToVcuMessage(String id, int desiredVelocity) {
        // Call the superclass constructor
        super(id);
        this.desiredVelocity = desiredVelocity;
    }


    // Override the constructData method
    @Override
    public void constructData() {


        data[0] = (byte) 0;
        data[1] = (byte) 0;
        data[2] = (byte) 0;
        data[3] = (byte) desiredVelocity;
        data[4] = (byte) 0;
        data[5] = (byte) 0;
        data[6] = (byte) 0;
        data[7] = (byte) 0;
    }

    public  void validateInputs() throws Exception{}


}

并在控制器中将

new PcToVcuMessage("222")
替换为

new PcToVcuMessage("222", Integer.parseInt(inDesiredVelocity.getText()))

然后只需从

extends MainViewController
类中删除
CanMessage
即可。这显然完全没有意义(消息不是控制 UI 的东西)。

一些与您的代码不相关的问题:

  1. 不要使用文本字段注册按键处理程序。要处理用户按下 Enter,请以通常的方式使用操作处理程序。
  2. 适当地命名类和变量。类名应该是名词(它们代表事物)。
    CanMessage
    是动词(或动词短语)。可能
    Message
    更合适,但我还是不太明白你在这里建模的内容。
© www.soinside.com 2019 - 2024. All rights reserved.