JavaFX ComboBox 不显示 COM 端口
JavaFX ComboBox not displaying COM ports
我使用 Arduino 和几个组件创建了一个电路,还在 JavaFX 中创建了一个小型测试应用程序(代码所示)。问题是我无法得到
COM 端口(使用 Windows 10)显示在组合框中,但我可以在输出控制台(COM5 上的 Arduino)中看到它们。当我运行代码检查时
在intellij我得到
"Problem synopsis - Unchecked call to 'addListener(ChangeListener? super T>)' as a member of raw type 'javafx.beans.value.ObservableValue' (at line 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 代码class:
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);
}
}
根据您的代码:
@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 在他的回答中所述,虽然可以像您所做的那样合并 Application
和 Controller
class 内容,但它不允许您访问从 start()
方法注入节点,任何调用 comboBoxPorts
的尝试都会抛出 NPE,因为您将处理 class 的两个不相关实例(一个由启动器创建, 另一个由 FXMLLoader).
通常的方法是使用 initialize
方法创建适当的 Controller
class,同时从 Application.start()
方法加载 fxml。
控制器class
@FXML
ComboBox comboBoxPorts;
public void initialize() {
...
comboBoxPorts.setItems(portList);
}
编辑 2
如果您仍想使用原来的单一 class 方法,这将起作用:
去掉fxml文件中的fx:controller
标签,将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);
}
我不是 JavaFx 专家,但我认为您混淆了应用程序和控制器这两个概念。如果控制器未正确初始化,José Pereda 建议的更改可能会导致 NullPointerException。
我已经使用您的 fxml 文件成功启动了一个小演示,并将您的控制器更改为不调用 arduino。但是我使用了额外的 class 来启动场景。这样,@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 专家,但也许您可以将其中一些更改应用到您的代码中。
我使用 Arduino 和几个组件创建了一个电路,还在 JavaFX 中创建了一个小型测试应用程序(代码所示)。问题是我无法得到 COM 端口(使用 Windows 10)显示在组合框中,但我可以在输出控制台(COM5 上的 Arduino)中看到它们。当我运行代码检查时 在intellij我得到 "Problem synopsis - Unchecked call to 'addListener(ChangeListener? super T>)' as a member of raw type 'javafx.beans.value.ObservableValue' (at line 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 代码class:
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);
}
}
根据您的代码:
@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 在他的回答中所述,虽然可以像您所做的那样合并 Application
和 Controller
class 内容,但它不允许您访问从 start()
方法注入节点,任何调用 comboBoxPorts
的尝试都会抛出 NPE,因为您将处理 class 的两个不相关实例(一个由启动器创建, 另一个由 FXMLLoader).
通常的方法是使用 initialize
方法创建适当的 Controller
class,同时从 Application.start()
方法加载 fxml。
控制器class
@FXML
ComboBox comboBoxPorts;
public void initialize() {
...
comboBoxPorts.setItems(portList);
}
编辑 2
如果您仍想使用原来的单一 class 方法,这将起作用:
去掉fxml文件中的fx:controller
标签,将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);
}
我不是 JavaFx 专家,但我认为您混淆了应用程序和控制器这两个概念。如果控制器未正确初始化,José Pereda 建议的更改可能会导致 NullPointerException。
我已经使用您的 fxml 文件成功启动了一个小演示,并将您的控制器更改为不调用 arduino。但是我使用了额外的 class 来启动场景。这样,@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 专家,但也许您可以将其中一些更改应用到您的代码中。