18

我使用 JavaFX 2.1 并使用 FXML 创建了 GUI,在我添加的这个 GUI 的控制器中myTextField.requestFocus();

但我总是把焦点放在另一个控件上。

4

7 回答 7

63

initialize()控件的时候还没有准备好处理焦点。

您可以尝试下一个技巧:

@Override
public void initialize(URL url, ResourceBundle rb) {
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            tf.requestFocus();
        }
    });
}

对于棘手的复杂应用程序(如评论中的 Pavel_K),您可能需要多次重复此例程并调用下一个方法行:

private void requestFocusOrDieTrying(Node node) {
    Platform.runLater(() -> {
        if (!node.isFocused()) {
            node.requestFocus();
            requestFocusOrDieTrying(node);
        }
    });
}

请注意,这是未记录的方法,如果在未来的 Java 版本中发生更改或中断,添加重复限制以避免无限循环可能是明智之举。比整个应用程序更好地失去焦点。:)


具有所述阈值的示例:

@Override
public void requestFocus() {
  requestFocus( getNode(), 3 );
}

private void requestFocus( final Node node, final int max ) {
  if( max > 0 ) {
    runLater(
        () -> {
          if( !node.isFocused() ) {
            node.requestFocus();
            requestFocus( node, max - 1 );
          }
        }
    );
  }
}
于 2012-10-05T10:57:05.613 回答
12

与@Sergey Grinev 完全相同的答案。确保您的 java 版本是最新的(JDK 1.8 或更高版本)。

Platform.runLater(()->myTextField.requestFocus());
于 2016-08-11T15:24:04.693 回答
10

如果你请求Focus(); 初始化场景后,它会工作!

像这样:

Stage stage = new Stage();
GridPane grid = new GridPane();
//... add buttons&stuff to pane

Scene scene = new Scene(grid, 800, 600);

TEXTFIELD.requestFocus();

stage.setScene(scene);
stage.show();

我希望这有帮助。:)

于 2013-08-25T08:31:25.917 回答
4

当尚未设置节点的场景属性时,可能会发生这种情况。唉,设置场景属性可能需要“很长时间”。

首次创建场景时,子节点的场景属性滞后,并且当项目添加到某些父节点时,例如 TabPane(奇怪的是,有些父节点似乎免疫,我不确定为什么)。

正确的策略,一直对我有用:

if (myNode.scene) == null {
    // listen for the changes to the node's scene property,
    // and request focus when it is set
} else {
    myNode.requestFocus()
}

我有一个方便的 Kotlin 扩展函数可以做到这一点。

fun Node.requestFocusOnSceneAvailable() {
    if (scene == null) {
        val listener = object : ChangeListener<Scene> {
            override fun changed(observable: ObservableValue<out Scene>?, oldValue: Scene?, newValue: Scene?) {
                if (newValue != null) {
                    sceneProperty().removeListener(this)
                    requestFocus()
                }
            }
        }
        sceneProperty().addListener(listener)
    } else {
        requestFocus()
    }
}

然后,您可以从代码中调用它,如下所示:

myNode.requestFocusOnSceneAvailable()

也许有人想把它翻译成Java。

于 2018-09-17T17:48:38.727 回答
1

我使用 JavaFX 11 遇到了同样的问题,并以 nickthecoder 提出的类似方式解决了它。

ChangeListener<Scene> sceneListener = new ChangeListener<Scene>() {
    @Override
    public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
        if (newValue != null) {
            editInput.requestFocus();
            editInput.sceneProperty().removeListener(this);
        }
    }
};
editInput.sceneProperty().addListener(sceneListener);

基本上只需向节点的场景属性添加一个侦听器,并在设置场景后在该侦听器请求焦点中。我还以这样一种方式编写了它,即在调用它之后将删除侦听器。

于 2020-06-10T11:41:45.540 回答
1

我宁愿使用计时器来强制关注文本字段。检查文本字段是否具有焦点的过程在单独的(后台)线程中完成。虽然请求焦点的过程是在 GUI 线程中完成的,但借助Platform.runLater().

//I'd rather using timer to enforce focus
Timer checkIfTFIsFocusedTimer = new Timer();
TimerTask checkIfTFIsFocusedTask = new TimerTask() {
    @Override
    public void run() {
        if (!textField.isFocused()) {
            Platform.runLater(() -> {                        
                textField.requestFocus();
            });
        } else {
            checkIfTFIsFocusedTimer.cancel();
        }
    }
};
checkIfTFIsFocusedTimer
        .scheduleAtFixedRate(checkIfTFIsFocusedTask,
        0, 100);
于 2021-07-13T09:23:34.580 回答
0

较旧的答案解释了Platform.runLater不工作的情况,但这个答案也涵盖了多个节点上的多个请求的情况。

问题是:scene属性变为非空节点的顺序,因此添加的侦听器被调用的顺序,不一定与添加两个侦听器的顺序相同。所以这个命令:

requestFocusOnSceneAvailable(node1)
requestFocusOnSceneAvailable(node2)

可能会意外导致以下顺序:

node2.requestFocus()
node1.requestFocus()

一个解决方案需要让侦听器requestFocus()仅在最近的节点上调用,可以使用静态变量进行跟踪:

private static Node nodeToRequestFocusOnOnceSceneAvailable;

public static void requestFocusOnceSceneAvailable(Node node) {
    
    // Remember this node as the latest node requested to receive focus.
    nodeToRequestFocusOnOnceSceneAvailable = node;
    
    // Schedule the focus request to happen whenever
    // JavaFX finally adds the node to the scene.
    Listeners.addAndFire(node.sceneProperty(), new ChangeListener<Scene>() {
        @Override
        public void changed(ObservableValue<? extends Scene> observable, Scene oldScene, Scene newScene) {
            if (newScene != null) {
                if (node == nodeToRequestFocusOnOnceSceneAvailable) {
                    node.requestFocus();
                    
                    // We no longer need to remember this node,
                    // since its focus has been requested.
                    nodeToRequestFocusOnOnceSceneAvailable = null;
                }
                
                // We no longer need the listener
                // after it has run once.
                observable.removeListener(this);
            }
        }
    });
}

请注意,此解决方案假定只有一个场景。

于 2020-07-21T02:48:42.603 回答