Java FX 等待用户输入

4
我正在使用JavaFX编写一个应用程序,并希望创建一个函数,等待用户在TextField中输入文本并按下Enter键后再返回(继续执行)。
private void setupEventHandlers() {
    inputWindow.setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent e) {
            if (e.getCode().equals(KeyCode.ENTER)) {
                inputWindow.clear();
            }
        }
    });
}

当用户按下回车键时,我清除TextField中的文本。

有什么想法吗?

编辑:我将明确我正在寻找的内容:

private void getInput() {
    do {
        waitForEventToFire();
    }
    while (!eventFired);
}

显然,这只是伪代码,但这就是我要找的东西。

好的。当用户输入文本时,事件处理程序将被调用。不清楚你的问题是什么...你在以上内容中要求什么? - user1676075
修改了我的问题。我想避免的是使用一个难看的 while 循环等待事件触发。 - Ethan Roseman
2个回答

6

示例解决方案

也许您想要做的是显示提示对话框并使用showAndWait等待从提示对话框中获取响应,然后再继续。类似于JavaFX2:我可以暂停后台任务/服务吗?

可能您的情况比后台任务服务简单一些(除非涉及长时间运行的任务),您可以在JavaFX应用程序线程上执行所有操作。我创建了一个简单的示例解决方案,它只在JavaFX应用程序线程上运行所有操作。

以下是示例程序的输出:

prompteddataprompt

每次遇到缺失数据时,都会显示提示对话框,并等待用户输入以填写缺失数据(在上面的屏幕截图中,用户提供的响应以绿色突出显示)。

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class MissingDataDemo extends Application {
  private static final String[] SAMPLE_TEXT = 
    "Lorem ipsum MISSING dolor sit amet MISSING consectetur adipisicing elit sed do eiusmod tempor incididunt MISSING ut labore et dolore magna aliqua"
    .split(" ");

  @Override public void start(Stage primaryStage) {
    VBox textContainer = new VBox(10);
    textContainer.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");

    primaryStage.setScene(new Scene(textContainer, 300, 600));
    primaryStage.show();

    TextLoader textLoader = new TextLoader(SAMPLE_TEXT, textContainer);
    textLoader.loadText();
  }

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

class TextLoader {
  private final String[] lines;
  private final Pane     container;

  TextLoader(final String[] lines, final Pane container) {
    this.lines = lines;
    this.container = container;
  }

  public void loadText() {
    for (String nextText: lines) {
      final Label nextLabel = new Label();

      if ("MISSING".equals(nextText)) {
        nextLabel.setStyle("-fx-background-color: palegreen;");

        MissingTextPrompt prompt = new MissingTextPrompt(
          container.getScene().getWindow()
        );

        nextText = prompt.getResult();
      }

      nextLabel.setText(nextText);

      container.getChildren().add(nextLabel);
    }              
  }

  class MissingTextPrompt {
    private final String result;

    MissingTextPrompt(Window owner) {
      final Stage dialog = new Stage();

      dialog.setTitle("Enter Missing Text");
      dialog.initOwner(owner);
      dialog.initStyle(StageStyle.UTILITY);
      dialog.initModality(Modality.WINDOW_MODAL);
      dialog.setX(owner.getX() + owner.getWidth());
      dialog.setY(owner.getY());

      final TextField textField = new TextField();
      final Button submitButton = new Button("Submit");
      submitButton.setDefaultButton(true);
      submitButton.setOnAction(new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent t) {
          dialog.close();
        }
      });
      textField.setMinHeight(TextField.USE_PREF_SIZE);

      final VBox layout = new VBox(10);
      layout.setAlignment(Pos.CENTER_RIGHT);
      layout.setStyle("-fx-background-color: azure; -fx-padding: 10;");
      layout.getChildren().setAll(
        textField, 
        submitButton
      );

      dialog.setScene(new Scene(layout));
      dialog.showAndWait();

      result = textField.getText();
    }

    private String getResult() {
      return result;
    }
  }
}

现有提示对话框库

ControlsFX 库中有一个预先编写的提示对话框,可以为您处理提示对话框的显示。

澄清事件处理和繁忙等待

您想要:

一个函数,等待用户在我文本字段中输入文本并按“enter”键。

按定义,这就是EventHandler的作用。 当发生此处理程序注册的特定事件时会触发 EventHandler。

当事件发生时,您的事件处理程序将被触发,并且您可以在事件处理程序中执行任何操作 - 您不需要,也不应该使用繁忙等待循环来等待事件。

创建TextField操作事件处理程序

与其像您在问题中所做的那样将事件处理程序放在窗口上,最好使用textField.setOnAction在您的文本字段上使用特定的操作事件处理程序:

textField.setOnAction(
  new EventHandler<ActionEvent>() {
    @Override public void handle(ActionEvent e) {
      // enter has been pressed in the text field.
      // take whatever action has been required here.
    }
);

如果您将文本字段放置在具有默认按钮集的对话框中,则无需为文本字段设置事件处理程序,因为对话框的默认按钮将适当地拾取并处理回车键事件。

哇!我真的很感激你的详细回复。谢谢。 - Ethan Roseman
我对JavaFX还很陌生,你刚刚帮我节省了很多时间!点赞 - Aashir

0

我本来想使用ControlsFx,但是由于无法升级到Java 1.8运行时,所以不得不从头开始构建对话框组件。以下是我设计的组件:

private static Response buttonSelected = Response.CANCEL;


/**
 * Creates a traditional modal dialog box
 *
 * @param owner       the calling Stage that is initiating the dialog.
 * @param windowTitle text that will be displayed in the titlebar
 * @param greeting    text next to icon that provides generally what to do (i.e. "Please enter the data below")
 * @param labelText   label text for the input box (i.e. "Number of widgets:")
 * @return If user clicks OK, the text entered by the user; otherwise if cancel, NULL.
 */
public static String prompt(final Stage owner, final String windowTitle, final String greeting, final String labelText) {
    //overall layout pane
    BorderPane root = new BorderPane();
    root.setPadding(PADDING);

    Scene scene = new Scene(root);

    final Dialog dial = new Dialog(windowTitle, owner, scene, MessageType.CONFIRM);

    final Button okButton = new Button("OK");
    okButton.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent e) {
            dial.close();
            buttonSelected = Response.YES;
        }
    });
    Button cancelButton = new Button("Cancel");
    cancelButton.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent e) {
            dial.close();
            buttonSelected = Response.NO;
        }
    });


    HBox headerGreeting = new HBox();
    headerGreeting.setSpacing(SPACING_SMALL);
    Text messageText = new Text(greeting);

    messageText.setFont(new Font(messageText.getFont().getName(), 14));
    headerGreeting.getChildren().addAll(icon, messageText);

    root.setTop(headerGreeting);

    //setup input controls
    HBox textHBox = new HBox(10);
    TextField input = new TextField();
    Label label = new Label();
    label.setText(labelText);
    label.setLabelFor(input);
    textHBox.getChildren().addAll(label, input);

    //create buttons
    HBox buttons = new HBox();
    buttons.setAlignment(Pos.CENTER);
    buttons.setSpacing(SPACING);
    buttons.getChildren().addAll(okButton, cancelButton);
    root.setCenter(buttons);

    //put controls and buttons in a vertical container, add to root component
    VBox container = new VBox(20);
    container.setPadding(new Insets(15, 12, 15, 12));
    container.getChildren().addAll(textHBox, buttons);
    root.setCenter(container);

    //handle enter key
    root.setOnKeyReleased(new EventHandler<KeyEvent>() {
        final KeyCombination combo = new KeyCodeCombination(KeyCode.ENTER);

        public void handle(KeyEvent t) {
            if (combo.match(t)) {
                okButton.fire();
            }
        }
    });

    input.requestFocus();   //set focus to the input box.

    dial.showDialog();
    if (buttonSelected.equals(Response.YES)) {
        return input.getText();
    }
    else { //cancel
        return null;
    }
}

我的测试工具看起来像这样,所以你可以很快地运行上面的代码:

import javafx.application.Application;
import javafx.stage.Stage;

public class FXOptionsPaneTest extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        String response = FXOptionsPane.prompt(primaryStage, "Create a new Study...", "Please enter the below information.", "Study Name:");
        System.out.println(response);
    }


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

虽然它不是一个通用的、复制粘贴的解决方案,但它是完整的,并且肯定可以编译。它直接从一个工作项目中复制而来。你可能需要组织导入并提供JavaFX运行库,但仅此而已。 - Domenic D.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接