Search code examples
javafxkeypress

JavaFX run a method once a specified key is pressed


I am trying to run a method in a controller class specified to a particular task, once a specified key is pressed using KeyListener. But i'm unable to detect the keypress and invoke the java.awt.event keyPressed method. My code is as follows :

public class POSController implements KeyListener {

@Override
public void keyPressed(java.awt.event.KeyEvent e) {
    if (e.getKeyCode() == com.sun.glass.events.KeyEvent.VK_F1) {
        try {
            paymentAction();
         } catch (Exception e1) {
            e1.printStackTrace();
       }
     }
  }
}

What could have gone wrong? Thanks in advance.

Here is the minimal executable example of the problem.

public class POSController implements KeyListener {

@FXML
private TableView<Product> productTableView;
@FXML
private TableView<Item> listTableView;
@FXML
private MenuItem logoutItem, profile;
@FXML
private javafx.scene.image.ImageView backImage;
@FXML
private MenuButton menuButton;
@FXML
private TableColumn<Item, String> itemColumn;
@FXML
private ComboBox<String> clientId, paymentMethod;
@FXML
private TableColumn<Item, Double> priceColumn, totalColumn, discountPercentageColumn, amountColumn;
@FXML
private TableColumn<Item, Integer> quantityColumn;
@FXML
private TableColumn<Product, String> productColumn;
@FXML
private TextField searchField,discountPercentage,productField,priceField,quantityField,vatPercentage,subTotalField,discountField,totalVatField,vatField,netPayableField,totalDiscountField;
@FXML
private TextField ;
@FXML
private TextField ;
@FXML
private TextField ;
@FXML
private TextField ;
@FXML
private TextArea descriptionArea;
@FXML
private Button addButton, removeButton, paymentButton, resetTableButton, resetButton;
@FXML
private Label quantityLabel, errorLabel, userName, backLabel;
@FXML
private ObservableList<Item> ITEMLIST;

public static Scene paymentScene;
private double xOffset = 0;
private double yOffset = 0;
public static double finalNetPayablePrice = 0.0;
public static double finalSubTotalPrice = 0.0;
public static double finalVat = 0.0;
public static double finalDiscount = 0.0;
public static String clientName = null;
public static String selectedPaymentMethod = null;
public static List<String> itemNames = new ArrayList<>();
public static List<Double> itemDiscounts = new ArrayList<>();
public static List<String> prices = new ArrayList<>();
public static List<String> quantities = new ArrayList<>();
public static List<String> subTotals = new ArrayList<>();
public static ObservableList<Item> itemList;
public static List<String> columnItemData = new ArrayList<>();
public static List<String> columnQuantityData = new ArrayList<>();

@FXML
private void initialize() throws SQLException, ClassNotFoundException, IOException {

ObservableList<Product> productsData = ProductDAO.searchGoodProducts(app.values.getProperty("STATUS_TYPE1"));
populateProducts(productsData);

 }

@FXML
private void populateProducts(ObservableList<Product> productData) throws ClassNotFoundException {
    productTableView.setItems(productData);
}

@Override
public void keyTyped(java.awt.event.KeyEvent e) {

}

@Override
public void keyPressed(java.awt.event.KeyEvent e) {

    if (e.getKeyCode() == java.awt.event.KeyEvent.VK_F1) {

        try {
            paymentAction();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

}

@Override
public void keyReleased(java.awt.event.KeyEvent e) {

}

@FXML
public void paymentAction() throws Exception {

    if (validateInputsForPayment()) {
        Payment payment = new Payment();
        FXMLLoader loader = new FXMLLoader((getClass().getResource(app.values.getProperty("INVOICE_VIEW_LOCATION"))));
        Parent root = loader.load();
        Stage stage = new Stage();
        root.setOnMousePressed((MouseEvent e) -> {
            xOffset = e.getSceneX();
            yOffset = e.getSceneY();
        });
        root.setOnMouseDragged((MouseEvent e) -> {
            stage.setX(e.getScreenX() - xOffset);
            stage.setY(e.getScreenY() - yOffset);
        });
        Scene scene = new Scene(root);
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.initStyle(StageStyle.UNDECORATED);
        stage.setScene(scene);
        this.paymentScene = scene;
        stage.showAndWait();
    }
}

Solution

  • You shouldn't be using java.awt.event.KeyListener for a JavaFX application. JavaFX has its own set of event API.

    Assuming that POSController is a controller class for a particular FXML:

    public class POSController {
        @FXML private BorderPane root; // Or any other Node from FXML file
    
        @FXML private void initialize() {
            javafx.event.EventHandler<javafx.scene.input.KeyEvent> handler = event -> {
                if (event.getCode() == javafx.scene.input.KeyCode.F1) {
                    try {
                        paymentAction();
                    } catch (Exception e1) {
                        e1.printStackTrace();
                    }
                }
            };
    
            // I'm using root to get scene, but any node would be fine
            if (root.getScene() != null) {
                root.getScene().addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, handler);
            }
            else {
                root.sceneProperty().addListener((obs, oldScene, newScene) -> {
                    if (newScene != null) {
                        root.getScene().addEventHandler(javafx.scene.input.KeyEvent.KEY_PRESSED, handler);
                    }
                });
            }
        }
    }
    

    This will add the key event to the Scene. If you do not need to apply this event scene-wide, then you can add the event handler at other appropriate nodes.

    Update

    If there are any input controls in the scene, then you may need to use setEventFilter() instead of setEventHandler(). This is because those controls are probably going to consume the key event during the event bubbling phase.