【问题标题】:JavaFX ComboBox not displaying COM portsJavaFX ComboBox 不显示 COM 端口
【发布时间】:2026-02-16 07:15:01
【问题描述】:

我使用 Arduino 和几个组件创建了一个电路,还在 JavaFX 中创建了一个小型测试应用程序(代码显示)。问题是我拿不到 COM 端口(使用 Windows 10)显示在组合框中,但我可以在输出控制台(COM5 上的 Arduino)中看到它们。当我运行代码检查时 在intellij我得到 “问题概要 - 未经检查地调用 'addListener(ChangeListener?super T>)' 作为原始类型 'javafx.beans.value.ObservableValue' 的成员(第 92 行)” 这似乎有用。我认为这意味着听众不再在听,但老实说我不知道​​。

fxml 文件代码:

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

    <?import javafx.geometry.Insets?>
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.ComboBox?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.control.TextField?>
    <?import javafx.scene.image.ImageView?>
    <?import javafx.scene.layout.BorderPane?>
    <?import javafx.scene.layout.HBox?>
    <?import javafx.scene.layout.VBox?>
    <?import javafx.scene.text.Font?>

    <BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="538.0" prefWidth="734.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="labrat.Controller">
       <top>
          <VBox prefHeight="148.0" prefWidth="723.0" style="-fx-background-color: #1f3641;" BorderPane.alignment="CENTER">
             <children>
                <HBox prefHeight="25.0" prefWidth="723.0" style="-fx-background-color: #d2d4df;">
                   <children>
                      <Label text="LabRat Version R" textFill="#5b5b5b">
                         <font>
                            <Font size="14.0" />
                         </font>
                         <HBox.margin>
                            <Insets bottom="5.0" left="3.0" right="3.0" top="3.0" />
                         </HBox.margin>
                      </Label>
                   </children>
                </HBox>
                <HBox>
                   <children>
                      <Button fx:id="changeText" mnemonicParsing="false" onAction="#setChangeText" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #ffffff;" text="Change text" textFill="#4a4a4a">
                         <HBox.margin>
                            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                         </HBox.margin>
                         <font>
                            <Font size="16.0" />
                         </font>
                      </Button>
                      <Label fx:id="dynamicText" text="Default Text" textFill="WHITE">
                         <font>
                            <Font size="24.0" />
                         </font>
                         <HBox.margin>
                            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                         </HBox.margin>
                      </Label>
                   </children>
                   <VBox.margin>
                      <Insets top="10.0" />
                   </VBox.margin>
                </HBox>
                <HBox layoutX="10.0" layoutY="10.0">
                   <children>
                      <Button fx:id="addElement" mnemonicParsing="false" onAction="#setAddElement" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #ffffff;" text="Add element" textFill="#4a4a4a">
                         <HBox.margin>
                            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                         </HBox.margin>
                         <font>
                            <Font size="16.0" />
                         </font>
                      </Button>
                      <TextField fx:id="typeToAdd" prefHeight="25.0" prefWidth="231.0" promptText="type to add to combobox">
                         <HBox.margin>
                            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                         </HBox.margin>
                      </TextField>
                      <ComboBox fx:id="element" prefHeight="25.0" prefWidth="238.0" promptText="select element" style="-fx-background-color: #ffcc99;">
                         <HBox.margin>
                            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                         </HBox.margin>
                      </ComboBox>
                   </children>
                   <VBox.margin>
                      <Insets top="10.0" />
                   </VBox.margin>
                </HBox>
             </children>
          </VBox>
       </top>
       <center>
          <VBox prefHeight="200.0" prefWidth="100.0" style="-fx-background-color: #1f3641;" BorderPane.alignment="CENTER">
             <children>
                <HBox VBox.vgrow="ALWAYS">
                   <children>
                      <ImageView fx:id="imgVw" fitHeight="319.0" fitWidth="421.0" pickOnBounds="true" preserveRatio="true" />
                      <VBox prefHeight="200.0" prefWidth="100.0" style="-fx-background-color: #cc0000;" HBox.hgrow="ALWAYS">
                         <children>
                            <Label fx:id="labelValue" text="Label Value" textFill="WHITE">
                               <font>
                                  <Font size="18.0" />
                               </font>
                               <VBox.margin>
                                  <Insets bottom="20.0" />
                               </VBox.margin>
                            </Label>
                            <ComboBox fx:id="comboBoxPorts" prefHeight="25.0" prefWidth="277.0" promptText="COM PORTS" style="-fx-background-color: #bdc3c7;" styleClass="comboBox" stylesheets="@testSS.css" />
                         </children>
                         <padding>
                            <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
                         </padding>
                      </VBox>
                   </children>
                </HBox>
             </children>
          </VBox>
       </center>
       <bottom>
          <HBox prefHeight="66.0" prefWidth="723.0" style="-fx-background-color: #222222;" BorderPane.alignment="CENTER">
             <children>
                <Button fx:id="nextScene" mnemonicParsing="false" onAction="#setNextScene" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #d2d4df;" text="Next Scene">
                   <HBox.margin>
                      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
                   </HBox.margin>
                   <font>
                      <Font size="16.0" />
                   </font>
                </Button>
                <Button fx:id="exit" mnemonicParsing="false" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #ff0000;" text="Exit Lab" textFill="WHITE">
                   <HBox.margin>
                      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
                   </HBox.margin>
                   <font>
                      <Font size="16.0" />
                   </font>
                </Button>
                <Button fx:id="version" layoutX="190.0" layoutY="20.0" mnemonicParsing="false" onAction="#setVersion" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #ffffff;" text="Alert Version">
                   <font>
                      <Font size="16.0" />
                   </font>
                   <HBox.margin>
                      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
                   </HBox.margin>
                </Button>
                <Button fx:id="showTheCar" layoutX="360.0" layoutY="20.0" mnemonicParsing="false" onAction="#setShowTheCar" prefHeight="40.0" prefWidth="150.0" style="-fx-background-color: #ffffff;" text="Show the car!">
                   <font>
                      <Font size="16.0" />
                   </font>
                   <HBox.margin>
                      <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
                   </HBox.margin>
                </Button>
             </children>
          </HBox>
       </bottom>
    </BorderPane>

JavaFX 类的代码:

    package labrat;

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Node;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.stage.Stage;
    import jssc.SerialPort;
    import jssc.SerialPortEvent;
    import jssc.SerialPortException;
    import jssc.SerialPortList;

    import java.io.IOException;

    public class Controller extends Application
    {
        @FXML
        Button changeText;
        @FXML
        Button showTheCar;
        @FXML
        Label dynamicText;
        @FXML
        Button addElement;
        @FXML
        TextField typeToAdd;
        @FXML
        ComboBox<String> element;
        @FXML
        Button nextScene;
        @FXML
        Button exit;
        @FXML
        Button version;
        @FXML
        ImageView imgVw;
        @FXML
        private Image img;


        // for serial com
        SerialPort arduinoPort = null;

        ObservableList<String> portList;

        @FXML
        private Label labelValue;
        @FXML
        ComboBox comboBoxPorts;



        @Override
        public void start(Stage primaryStage)
        {
            Parent rootParent = null;
            try
            {
                rootParent = FXMLLoader.load(getClass().getResource("firstScene.fxml"));
                primaryStage.setTitle("Test Lab version 3200");
                primaryStage.setScene(new Scene(rootParent, 1100, 600));
                primaryStage.show();
                System.out.println("First Stage now showing");
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }

            labelValue = new Label();
            detectPort();
            comboBoxPorts = new ComboBox(portList);
            comboBoxPorts.valueProperty().addListener(new ChangeListener<String>() {

                @Override
                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
                    System.out.println("\nJust making sure this was executed!");
                    disconnectArduino();
                    connectArduino(newValue);
                }
            });
            /*
            comboBoxPorts.getItems().addAll(portList);
            if(comboBoxPorts.getItems().addAll(portList))
            {
                System.out.println("\nAdded port-list(from observable list) to the CB!");
            }

            if(!(comboBoxPorts.getItems().addAll(portList)))
            {
                System.out.println("\nPort-list not added to CB!");
            }
            */

        }

        @Override
        public void stop() throws Exception
        {
            disconnectArduino();
            super.stop();
        }

        // port detector method
        private void detectPort(){

            System.out.println("\n1/3 Now detecting port...");

            portList = FXCollections.observableArrayList();

            String[] serialPortNames = SerialPortList.getPortNames();
            for(String name: serialPortNames){

                System.out.println("\nDetected Port: ");
                System.out.println(name);
                portList.add(name);
            }
        }

        // connect the Arduino
        public boolean connectArduino(String port)
        {
            System.out.println("\n2/3 Connect Arduino now running...");

            boolean success = false;
            SerialPort serialPort = new SerialPort(port);
            try {
                serialPort.openPort();
                serialPort.setParams(
                        SerialPort.BAUDRATE_9600,
                        SerialPort.DATABITS_8,
                        SerialPort.STOPBITS_1,
                        SerialPort.PARITY_NONE);
                serialPort.setEventsMask(SerialPort.MASK_RXCHAR);
                serialPort.addEventListener((SerialPortEvent serialPortEvent) -> {
                    if(serialPortEvent.isRXCHAR()){
                        try {
                            String st = serialPort.readString(serialPortEvent
                                    .getEventValue());

                            System.out.println("\nSPE Listener: ");
                            System.out.println(st);

                            //Update label in ui thread
                            Platform.runLater(() -> {
                                System.out.println("\nAttempted to update label in ui thread");
                                labelValue.setText(st);
                            });

                        } catch (SerialPortException ex) {
                            ex.printStackTrace();
                        }
                    }
                });

                arduinoPort = serialPort;
                success = true;
            } catch (SerialPortException ex) {
                ex.printStackTrace();
                System.out.println("SerialPortException: " + ex.toString());
            }

            return success;
        }

        // disconnect the Arduino
        public void disconnectArduino()
        {
            System.out.println("\n3/3 Now disconnecting Arduino...");

            if(arduinoPort != null)
            {
                try
                {
                    arduinoPort.removeEventListener();

                    if(arduinoPort.isOpened())
                    {
                        arduinoPort.closePort();
                    }
                }
                catch (SerialPortException e)
                {
                    e.printStackTrace();
                }
            }
        }

        @FXML
        public void setChangeText()
        {
            dynamicText.setText("Text changed successfully!");
        }

        @FXML
        public void setShowTheCar()
        {
            img = new Image("labrat/images/megane.jpg");
            imgVw.setImage(img);
            imgVw.isPreserveRatio();
        }

        @FXML
        public void setVersion()
        {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setTitle("LabRat Version");
            alert.setContentText("Version after the previous one! v0.001");
            alert.showAndWait();
        }

        @FXML
        public void setNextScene(ActionEvent ev)
        {
            try
            {
                Parent secondParent = FXMLLoader.load(getClass().getResource("secondScene.fxml"));
                Scene secondScene = new Scene(secondParent);
                Stage ourStage = (Stage) ((Node) ev.getSource()).getScene().getWindow();
                ourStage.setTitle("Test Lab Page II");
                ourStage.setScene(secondScene);
                ourStage.show();
            }

            catch (IOException e)
            {
                e.printStackTrace();
            }
        }

        @FXML
        public void setAddElement()
        {
            String bufferText = typeToAdd.getText();
            element.getItems().addAll(bufferText);
        }
    }

显示场景时的控制台输出:

    "C:\Program Files\Java\jdk1.8.0_73\bin\java" (cut this short to minimize length)
    First Stage now showing

    1/3 Now detecting port...

    Detected Port: 
    COM5

如何让 ComboBox 显示端口?编辑 - 我正在使用 jSSc 插件版本 2.8.0

编辑 - 在@RubioRic 和@Jose Pereda 的建议之后对控制器的更正更改 - 现在显示端口(最终代码):

            package labrat;

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.fxml.Initializable;
    import javafx.scene.Node;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.stage.Stage;
    import jssc.SerialPort;
    import jssc.SerialPortEvent;
    import jssc.SerialPortException;
    import jssc.SerialPortList;

    import java.io.IOException;
    import java.net.URL;
    import java.util.ResourceBundle;

    public class Controller implements Initializable
    {
        @FXML
        Button changeText;
        @FXML
        Button showTheCar;
        @FXML
        Label dynamicText;
        @FXML
        Button addElement;
        @FXML
        TextField typeToAdd;
        @FXML
        ComboBox<String> element;
        @FXML
        Button nextScene;
        @FXML
        Button exit;
        @FXML
        Button version;
        @FXML
        ImageView imgVw;
        @FXML
        private Image img;


        // for serial com
        SerialPort arduinoPort = null;

        ObservableList<String> portList;

        @FXML
        private Label labelValue;
        @FXML
        ComboBox comboBoxPorts;


        @Override
        public void initialize(URL location, ResourceBundle resources)
        {
            detectPort();
        }

        // port detector method
        private void detectPort(){

            System.out.println("\n1/3 Now detecting port...");

            portList = FXCollections.observableArrayList();

            String[] serialPortNames = SerialPortList.getPortNames();
            for(String name: serialPortNames){

                System.out.println("\nDetected Port: ");
                System.out.println(name);
                portList.add(name);
            }

            // No need to create a new combo instance
            // No need to add a change listener to refresh ports
            comboBoxPorts.setItems(portList);
            comboBoxPorts.valueProperty().addListener(new ChangeListener<String>() {
                @Override
                public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
                {
                    System.out.println("\nChangeListener executed!");
                    disconnectArduino();
                    connectArduino(newValue);
                    System.out.println("\nOld Value was: " + oldValue);
                    System.out.println("\nNew Value is: " + newValue);
                    labelValue.setText(newValue);
                }
            });
        }

        // connect the Arduino
        public boolean connectArduino(String port)
        {
            System.out.println("\n2/3 Connect Arduino now running...");

            boolean success = false;
            SerialPort serialPort = new SerialPort(port);
            try {
                serialPort.openPort();
                serialPort.setParams(
                        SerialPort.BAUDRATE_9600,
                        SerialPort.DATABITS_8,
                        SerialPort.STOPBITS_1,
                        SerialPort.PARITY_NONE);
                serialPort.setEventsMask(SerialPort.MASK_RXCHAR);
                serialPort.addEventListener((SerialPortEvent serialPortEvent) -> {
                    if(serialPortEvent.isRXCHAR()){
                        try {
                            String st = serialPort.readString(serialPortEvent
                                    .getEventValue());

                            System.out.println("\nSPE Listener: ");
                            System.out.println(st);

                            //Update label in ui thread
                            Platform.runLater(() -> {
                                System.out.println("\nAttempted to update label in ui thread");
                                labelValue.setText(st);
                            });

                        } catch (SerialPortException ex) {
                            ex.printStackTrace();
                        }
                    }
                });

                arduinoPort = serialPort;
                success = true;
            } catch (SerialPortException ex) {
                ex.printStackTrace();
                System.out.println("SerialPortException: " + ex.toString());
            }

            return success;
        }

        // disconnect the Arduino
        public void disconnectArduino()
        {
            System.out.println("\n3/3 Now disconnecting Arduino...");

            if(arduinoPort != null)
            {
                try
                {
                    arduinoPort.removeEventListener();

                    if(arduinoPort.isOpened())
                    {
                        arduinoPort.closePort();
                    }
                }
                catch (SerialPortException e)
                {
                    e.printStackTrace();
                }
            }
        }

        @FXML
        public void setChangeText()
        {
            dynamicText.setText("Text changed successfully!");
        }

        @FXML
        public void setShowTheCar()
        {
            img = new Image("labrat/images/megane.jpg");
            imgVw.setImage(img);
            imgVw.isPreserveRatio();
        }

        @FXML
        public void setVersion()
        {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setTitle("LabRat Version");
            alert.setContentText("Version after the previous one! v0.001");
            alert.showAndWait();
        }

        @FXML
        public void setNextScene(ActionEvent ev)
        {
            try
            {
                Parent secondParent = FXMLLoader.load(getClass().getResource("secondScene.fxml"));
                Scene secondScene = new Scene(secondParent);
                Stage ourStage = (Stage) ((Node) ev.getSource()).getScene().getWindow();
                ourStage.setTitle("Test Lab Page II");
                ourStage.setScene(secondScene);
                ourStage.show();
            }

            catch (IOException e)
            {
                e.printStackTrace();
            }
        }

        @FXML
        public void setAddElement()
        {
            String bufferText = typeToAdd.getText();
            element.getItems().addAll(bufferText);
        }

    }

【问题讨论】:

    标签: java intellij-idea javafx arduino fxml


    【解决方案1】:

    我不是 JavaFx 专家,但我认为您将应用程序和控制器这两个概念混为一谈。如果控制器未正确初始化,José Pereda 建议的更改可能会导致 NullPointerException。

    我已经使用您的 fxml 文件成功启动了一个小演示,并将您的控制器更改为不调用 arduino。但是我已经使用了额外的课程来启动场景。这样@FMX 元素就会被正确注入,并且组合框会显示端口。

    主要

     import javafx.application.Application;
     import javafx.fxml.FXMLLoader;
     import javafx.scene.Parent;
     import javafx.scene.Scene;
     import javafx.stage.Stage;
    
     public class ComboMain extends Application {
          @Override
          public void start(Stage primaryStage) throws Exception{
              Parent root = FXMLLoader.load(getClass().getResource("combo.fxml"));
              primaryStage.setTitle("Combo");
              primaryStage.setScene(new Scene(root));
              primaryStage.show();
          }
    
          public static void main(String[] args) {
              launch(args);
          }
     }
    

    控制器

      public class ComboController implements Initializable {
            // @FXML elements here
    
           @Override
           public void initialize(URL arg0, ResourceBundle arg1) {
                 detectPort(); // Include all the code that you need for initializing elements here
           }
    
          // port detector method
          // Dummy method - Include arduino calls here
          private void detectPort(){
    
               System.out.println("\n1/3 Now detecting port...");
    
               portList = FXCollections.observableArrayList();
    
               String[] serialPortNames = {"COM1", "COM2", "COM3"}; //    SerialPortList.getPortNames();
    
               for(String name: serialPortNames){
                   System.out.println("\nDetected Port: ");
                   System.out.println(name);
                   portList.add(name);
               }
    
               // No need to create a new combo instance
               // No need to add a change listener to refresh ports
               comboBoxPorts.setItems(portList); 
           }
    
           // Arduino methods here
      }
    

    正如我所说,我不是 javafx 专家,但也许您可以将其中一些更改应用于您的代码。

    【讨论】:

    • @RubiRic, @ Jose 编辑了我的控制器 - 谢谢大家。该端口现在显示在 ComboBox 中。如果我能打两个答案就好了!!!
    • @tpmabb 掷硬币。头! ;-P
    • 在我翻转之前 :) 我现在无法让标签来监听更改,因为我们摆脱了更改监听器 - 我不是一个非常有经验的程序员,所以我们如何才能恢复它?我们可以把它放在initialize()方法中吗?
    • @tpmabb 是的,这是一个合适的地方。我错过了您正在更改这些方法中的标签。我以为你只是用它来改变组合内容。
    【解决方案2】:

    根据您的代码:

    @FXML
    ComboBox comboBoxPorts;
    
    @Override
    public void start(Stage primaryStage)
    {
        ...
        comboBoxPorts = new ComboBox(portList);
    }
    

    start() 方法中,您正在创建comboBoxPorts 的第二个实例。这是获取端口列表的那个,但是这没有添加到场景图中。

    相反,由于@FXML 注释,您不会向第一个实例添加任何内容,即场景图上由FXMLLoader 创建的实例。

    你只需要添加列表:

    @FXML
    ComboBox comboBoxPorts;
    
    @Override
    public void start(Stage primaryStage)
    {
        ...
        comboBoxPorts.setItems(portList);
    }
    

    编辑

    正如@RubioRic 在他的回答中所说,虽然可以像您所做的那样合并ApplicationController 类内容,但它不允许您从start() 方法访问注入的节点,并且任何调用 comboBoxPorts 的尝试都会引发 NPE,因为您将处理该类的两个不相关实例(一个由启动器创建,另一个由 FXMLLoader 创建)。

    通常的方法是创建一个适当的Controller 类,使用initialize 方法,同时从Application.start() 方法加载fxml。

    控制器类

    @FXML
    ComboBox comboBoxPorts;
    
    public void initialize() {
        ...
        comboBoxPorts.setItems(portList);
    }
    

    编辑 2

    如果您仍想使用原来的单类方法,这将起作用:

    从 fxml 文件中删除 fx:controller 标签,并在 start() 方法上设置控制器,参考 this,这样您将只有一个实例:

    @FXML
    ComboBox comboBoxPorts;
    
    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("firstScene.fxml"));
        loader.setController(this);
        Parent root = loader.load();
        ...
        comboBoxPorts.setItems(portList);
    }
    

    【讨论】:

    • 谢谢。我尝试了您的更正,但现在我遇到了一个异常 - 检测到端口:应用程序启动方法 3/3 中的 COM5 异常现在断开 Arduino ......线程“主”java.lang.NoSuchMethodException 中的异常:labrat.Controller.main([ Ljava.lang.String;) at java.lang.Class.getMethod(Class.java:1786) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:119) 进程以退出代码 1
    • 如果我将代码放在 try catch 块中,我会在 labrat.Controller.start(Controller.java:85) 处得到一个空指针异常 -java.lang.NullPointerException
    • 我认为控制器没有正确初始化。请大家看看我的回答。可能有用,或者我弄错了。
    • @RubioRic 你是对的。我应该提到控制器应该移动到一个适当的类中,它不能在Application 类中混合。
    • comboBoxPorts.setItems(portList);是第 85 行。感谢控制器更正,我现在可以在同一个类中实现 Initializable 还是移动它至关重要?