使用JavaFX中的箭头键导航到GridPane中的TextField [英] Navigating out of a TextField in a GridPane using the arrow keys in JavaFX

查看:94
本文介绍了使用JavaFX中的箭头键导航到GridPane中的TextField的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用JavaFX库用Java编写一个sodoku解算器程序.该程序在 GridPane 中集成了一个交互式数独板,该板由一系列 TextField 组成.董事会看起来像这样:

I am making a sodoku solver program in Java with the JavaFX library. The program incorporates an interactive sodoku board consisting of a series of TextFields in a GridPane. The board looks like this:

现在,光标位于最左上方的 TextField 中.如果该字段中包含文本,则用户将能够使用箭头键在文本中移动光标.但是,我希望用户能够使用箭头键导航到其他 TextField .问题是,该字段处于键入模式"状态.(我不知道官方术语),所以箭头键只能将光标移动到文本中的其他点,否则它将停留在同一字段中.

Right now, the cursor is in the top left most TextField. If the field had text in it, the user would be able to move the cursor through the text by using the arrow keys. However, I want the user to be able to use the arrow keys to navigate to a different TextField. The issue is, the field is in "typing mode" (I don't know the official terminology) so the arrow keys only move the cursor to a different point in the text, but otherwise it stays in the same field.

这是我的意思:

假设我画的线是光标.现在,如果单击左箭头键,光标将移至1的左侧,但我希望它向左移动 TextField .如果单击向下箭头键,则不会发生任何事情,因为光标下方的1不会有任何文本可供浏览,但我希望它移至下面的 TextField .

Pretend that line I drew is the cursor. Right now, if I click the left arrow key, the cursor will move to the left of the 1, but I want it to move the the TextField on the left, instead. If I click the down arrow key, nothing happens because there is no text below the 1 for the cursor to navigate to, but I want it to move to the TextField below, instead.

GridPane 的代码是这样的:

TextField[][] squares = new TextField[9][9];
GridPane grid = new GridPane();
for (int i = 0; i < 9; i++) {
    for (int j = 0; j < 9; j++) {
        squares[i][j] = new TextField();
        squares[i][j].setPrefHeight(8);
        squares[i][j].setPrefWidth(25);
        grid.add(squares[i][j], j, i);
     }
}
grid.setAlignment(Pos.CENTER);

对于我来说, squares 数组可以访问 GridPane 中的各个 TextField .

The squares array is for me to have access to individual TextFields in the GridPane.

关于如何解决此问题的任何建议?

Any suggestions for how I can fix this?

推荐答案

要完全避免集中的 TextField 处理箭头键,您需要先拦截 KeyEvent 到达所说的 TextField .这可以通过向 GridPane 中添加事件 filter 并酌情使用该事件来实现.如果不确定为什么这样,您可以查看

To avoid the focused TextField from handling arrow keys at all you need to intercept the KeyEvent before it reaches said TextField. This can be accomplished by adding an event filter to the GridPane and consuming the event as appropriate. If you're not sure why this works you can check out the JavaFX: Handling Events tutorial.

然后,您可以使用 Node#requestFocus()以编程方式更改焦点节点.

Then you can use Node#requestFocus() to programmatically change the focused node.

我还建议设置每个 TextField prefColumnCount ,而不是尝试手动设置首选尺寸.这样,首选尺寸将根据字体大小进行计算.

I also recommend setting the prefColumnCount of each TextField rather than trying to set the preferred dimensions manually. That way the preferred dimensions are computed based on the font size.

这是一个例子:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class App extends Application {

  private TextField[][] fields;

  @Override
  public void start(Stage primaryStage) {
    GridPane grid = new GridPane();
    grid.setHgap(3);
    grid.setVgap(3);
    grid.setPadding(new Insets(5));
    grid.setAlignment(Pos.CENTER);
    grid.addEventFilter(KeyEvent.KEY_PRESSED, this::handleArrowNavigation);

    fields = new TextField[9][9];

    for (int i = 0; i < 9; i++) {
      for (int j = 0; j < 9; j++) {
        fields[i][j] = createTextField();
        grid.add(fields[i][j], j, i);
      }
    }

    primaryStage.setScene(new Scene(grid));
    primaryStage.show();
  }

  private void handleArrowNavigation(KeyEvent event) {
    Node source = (Node) event.getSource(); // the GridPane
    Node focused = source.getScene().getFocusOwner();
    if (event.getCode().isArrowKey() && focused.getParent() == source) {
      int row = GridPane.getRowIndex(focused);
      int col = GridPane.getColumnIndex(focused);
      // Switch expressions were standardized in Java 14
      switch (event.getCode()) {
        case LEFT -> fields[row][Math.max(0, col - 1)].requestFocus();
        case RIGHT -> fields[row][Math.min(8, col + 1)].requestFocus();
        case UP -> fields[Math.max(0, row - 1)][col].requestFocus();
        case DOWN -> fields[Math.min(8, row + 1)][col].requestFocus();
      }
      event.consume();
    }
  }

  private TextField createTextField() {
    TextField field = new TextField();
    // Rather than setting the pref sizes manually this will
    // compute the pref sizes based on the font size.
    field.setPrefColumnCount(1);
    field.setFont(Font.font(20));
    field.setTextFormatter(
        new TextFormatter<>(
            change -> {
              // Only allow the text to be empty or a single digit between 1-9
              if (change.getControlNewText().matches("[1-9]?")) {
                // Without this the text goes "off screen" to the left. This also
                // seems to have the added benefit of selecting the just-entered
                // text, which makes replacing it a simple matter of typing another
                // digit.
                change.setCaretPosition(0);
                return change;
              }
              return null;
            }));
    return field;
  }
}

上面还向每个 TextField 添加了 TextFormatter ,以显示一种将文本限制为1到9之间的数字的方法.请注意,箭头导航不会自动换行"周围"当它到达行或列的末尾时.当然,如果需要,您当然可以修改代码以实现此目的.

The above also adds a TextFormatter to each TextField to show a way to limit the text to digits between 1 and 9. Note the arrow navigation does not "wrap around" when it reaches the end of a row or column. You can of course modify the code to implement this, if desired.

您可能要考虑为游戏创建模型.这样,业务逻辑就不会直接绑定到JavaFX UI对象.当您更新模型时,它将通知视图(可能通过视图模型",具体取决于体系结构),并且视图将相应地进行更新.

You may want to consider creating a model for the game. That way the business logic is not tied directly to JavaFX UI objects. When you update the model it would notify the view (possibly via a "view model", depending on the architecture) and the view will update itself accordingly.

这篇关于使用JavaFX中的箭头键导航到GridPane中的TextField的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆