我使用 JavaFX 2.1 并使用 FXML 创建了 GUI,在我添加的这个 GUI 的控制器中myTextField.requestFocus();
。
但我总是把焦点放在另一个控件上。
在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 );
}
}
);
}
}
与@Sergey Grinev 完全相同的答案。确保您的 java 版本是最新的(JDK 1.8 或更高版本)。
Platform.runLater(()->myTextField.requestFocus());
如果你请求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();
我希望这有帮助。:)
当尚未设置节点的场景属性时,可能会发生这种情况。唉,设置场景属性可能需要“很长时间”。
首次创建场景时,子节点的场景属性滞后,并且当项目添加到某些父节点时,例如 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。
我使用 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);
基本上只需向节点的场景属性添加一个侦听器,并在设置场景后在该侦听器请求焦点中。我还以这样一种方式编写了它,即在调用它之后将删除侦听器。
我宁愿使用计时器来强制关注文本字段。检查文本字段是否具有焦点的过程在单独的(后台)线程中完成。虽然请求焦点的过程是在 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);
较旧的答案解释了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);
}
}
});
}
请注意,此解决方案假定只有一个场景。