Elegant way to handle Keyboard Input in JavaFX

梦想与她 提交于 2019-12-20 06:05:19

问题


I am currently working on a calculator in JavaFX and right now trying to implement keyboard input support.

For this I am trying to implement an EventHandler in the main .java file like this:

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

    Scene scene_main = new Scene(root);

    stage.setScene(scene_main);
    stage.show();

    scene_main.setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent keyEvent) {

            switch (keyEvent.getCode()) {
                case ENTER:
                    System.out.println("Enter");
                    break;
                case ADD:
                    System.out.println("Plus");
                    break;
                case SUBTRACT:
                    System.out.println("Minus");
                    break;
                case DIVIDE:
                    System.out.println("Division");
                    break;
                case MULTIPLY:
                    System.out.println("Multiply");
                    break;

            }
        }
    }
    );
}

The FXMLDocumentController.java:

import java.net.URL;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
import java.util.Random;
import java.util.ResourceBundle;
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.Button;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Stage;

/**
 *
 * @author ilja
 */
public class FXMLDocumentController implements Initializable {

@FXML
private void handleTestAction(ActionEvent event) throws Exception {
    ((Node) (event.getSource())).getScene().getWindow().hide();
    Parent parent = FXMLLoader.load(getClass().getResource("/calculator/CrashPopup.fxml"));
    Stage stage = new Stage();
    Scene scene = new Scene(parent);
    stage.setScene(scene);
    stage.setTitle("Teeeeest");
    stage.show();
}

private enum State {

    EQUALED, FIRST, SECOND, OPERATOR
}

public static String operator = "+";
private double oldValue = 0;
private double newValue = 0;
private double result = 0;
private String oldText, newText;
private String digit;
private boolean equaled = false;
Random r = new Random();
int i1;

private State state = State.EQUALED;

NumberFormat nf = new DecimalFormat("##.###", new DecimalFormatSymbols(Locale.US));

@FXML
private TextField displayField;

@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO
    displayField.setText("0");
}

@FXML
private void handleDotAction(ActionEvent event) {

    double result;

    oldText = displayField.getText();

    if (!oldText.isEmpty() && !oldText.contains(".")) {
        newText = oldText + ".";
    } else {
        newText = oldText;
    }

    displayField.setText(newText);
    result = Double.parseDouble(newText);

    if (state == State.EQUALED || state == State.FIRST) {
        oldValue = result;
        state = State.FIRST;
    } else {
        newValue = result;
        state = State.SECOND;
    }
}

@FXML
private void handleDigitAction(ActionEvent event) {

    digit = ((Button) event.getSource()).getText();
    if (state == State.FIRST || state == State.SECOND) {
        oldText = displayField.getText();
    } else {
        oldText = "";
    }

    if ("0".equals(oldText) /*|| "0.0".equals(oldText)*/) {
        displayField.setText(digit);
    } else {
        displayField.setText(oldText + digit);
    }

    if (state == State.EQUALED || state == State.FIRST) {
        oldValue = Double.parseDouble(displayField.getText());
        state = State.FIRST;
    } else {
        newValue = Double.parseDouble(displayField.getText());
        state = State.SECOND;
    }
}

@FXML
private void handleOperator(ActionEvent event) {

    if (state == State.EQUALED) {
        operator = ((Button) event.getSource()).getText();
    }

    if (state == State.SECOND) {
        switch (operator) {
            case "+":
                oldValue += newValue;
                break;
            case "-":
                oldValue -= newValue;
                break;
            case "*":
                oldValue *= newValue;
                break;
            case "/":
                oldValue /= newValue;
                break;
            default:
                break;
        }
        result = oldValue;
        newValue = 0;
        displayField.setText(String.valueOf(nf.format(oldValue)));
    }

    operator = ((Button) event.getSource()).getText();
    state = State.OPERATOR;
}

@FXML
private void handleEqualAction(ActionEvent event) throws Exception{

    i1 = r.nextInt(6 - 0) + 0;

    if (i1 == 1) {
        ((Node) (event.getSource())).getScene().getWindow().hide();
        Parent parent = FXMLLoader.load(getClass().getResource("/calculator/CrashPopup.fxml"));
        Stage stage = new Stage();
        Scene scene = new Scene(parent);
        stage.setScene(scene);
        stage.setTitle("Unerwarteter Fehler");
        stage.show();
    }

    switch (operator) {
        case "+":
            oldValue += newValue;
            break;
        case "-":
            oldValue -= newValue;
            break;
        case "*":
            oldValue *= newValue;
            break;
        case "/":
            oldValue /= newValue;
            break;
        default:
            break;
    }
    result = oldValue;
    displayField.setText(String.valueOf(nf.format(oldValue)));

    state = State.EQUALED;

}

@FXML
private void handleClearAction(ActionEvent event) {
    displayField.setText("0");
    oldValue = 0;
    newValue = 0;
    operator = "+";
    state = State.EQUALED;
}

@FXML
private void handleClearEntryAction(ActionEvent event) {
    displayField.setText("0");
    newValue = 0;
    switch (state) {
        case EQUALED:
            displayField.setText(String.valueOf(nf.format(result)));
            break;
        case FIRST:
            oldValue = 0;
            state = State.EQUALED;
            break;
        case SECOND:
        case OPERATOR:
            newValue = 0;
            state = State.OPERATOR;
            break;
        default:
            break;
    }
}

private void handleZeroAction(ActionEvent event) {
    digit = ((Button) event.getSource()).getText();
    oldText = displayField.getText();
    if ("0".equals(oldText) || "0.0".equals(oldText)) {
        newText = oldText;
    } else {
        newText = oldText + digit;
    }
    displayField.setText(newText);
}
}

Now the problem is, that whenever those keys are pressed, the same things should happen as when I click the corresponding buttons in the GUI.

All digit-buttons share one onAction event and +,-,/,* also share one, etc. They all use variables in the FXMLDocumentController.java file.

So I am not sure what the best way to handle this is. Can I just invoke the onAction events from within the main.java file or should I copy paste (which would cause redundance) the method content into this file and make the variables public/protected?

I tried invoking the methods from within the main file but failed because I don't understand which parameters the method will get, as they require an ActionEvent object.


回答1:


If you refactor the handler methods so they just determine the value they represent, then call another method, e.g.:

@FXML
private void handleDigitAction(ActionEvent event) {

    String digit = ((Button) event.getSource()).getText();
    handleDigit(digit) ;
}

public void handleDigit(String digit) {
    if (state == State.FIRST || state == State.SECOND) {
        oldText = displayField.getText();
    } else {
        oldText = "";
    }

    if ("0".equals(oldText) /*|| "0.0".equals(oldText)*/) {
        displayField.setText(digit);
    } else {
        displayField.setText(oldText + digit);
    }

    if (state == State.EQUALED || state == State.FIRST) {
        oldValue = Double.parseDouble(displayField.getText());
        state = State.FIRST;
    } else {
        newValue = Double.parseDouble(displayField.getText());
        state = State.SECOND;
    }
}

then you can call the new method as needed:

@Override
public void start(Stage stage) throws Exception {
    FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
    Parent root = loader.load();
    FXMLDocumentController controller = loader.getController();

    Scene scene_main = new Scene(root);

    stage.setScene(scene_main);
    stage.show();

    scene_main.setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent keyEvent) {

            switch (keyEvent.getCode()) {
                case DIGIT1:
                    controller.handleDigit("1");
                    break ;
                case DIGIT2:
                    controller.handleDigit("2");
                    break ;
                case DIGIT3:
                    controller.handleDigit("3");
                    break ;
                case DIGIT4:
                    controller.handleDigit("4");
                    break ;
                case DIGIT5:
                    controller.handleDigit("5");
                    break ;
                case DIGIT6:
                    controller.handleDigit("6");
                    break ;
                case DIGIT7:
                    controller.handleDigit("7");
                    break ;
                case DIGIT8:
                    controller.handleDigit("8");
                    break ;
                case DIGIT9:
                    controller.handleDigit("9");
                    break ;

            }
        }
    }
    );
}

and so on.



来源:https://stackoverflow.com/questions/33129568/elegant-way-to-handle-keyboard-input-in-javafx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!