RequestFocus in TextField doesn't work

后端 未结 6 1224
隐瞒了意图╮
隐瞒了意图╮ 2021-02-13 02:11

I use JavaFX 2.1 and I created GUI using FXML, in the controller of this GUI I added myTextField.requestFocus();.

But I always get the focus in the other co

相关标签:
6条回答
  • 2021-02-13 02:16

    If you requestFocus(); after initializing the scene, it will work!

    Like this:

    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();
    

    I hope this helps. :)

    0 讨论(0)
  • 2021-02-13 02:18

    I ran into the same problem using JavaFX 11 and solved it in a similar way that nickthecoder proposed.

    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);
    

    Basicly just add a listener to the sceneProperty of the node and in that listener request focus once the scene is set. I also wrote it in such a way that the listener will be removed after it is invoked.

    0 讨论(0)
  • 2021-02-13 02:22

    This can occur when the Scene property for the Node is not yet set. Alas, the scene property can take a "long" time to be set.

    The child node's scene property lags when a scene is first created, and also, when items are added to some parents, such as a TabPane (oddly some parents seem immune, I'm not sure why).

    The correct strategy, which has always worked for me :

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

    I have a handy Kotlin extension function which does this.

    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()
        }
    }
    

    You can then call it from within you code like so :

    myNode.requestFocusOnSceneAvailable()
    

    Perhaps somebody would like to translate it to Java.

    0 讨论(0)
  • 2021-02-13 02:26

    At the time of initialize() controls are not yet ready to handle focus.

    You can try next trick:

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

    For tricky complex applications (like Pavel_K has in the comments) you may want to repeat this routine several times and call method line next one:

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

    Note this is the undocumented approach and it may be wise to add a limit for repetitions to avoid endless loop if something changed or broke in future Java releases. Better to lose focus than a whole app. :)


    Example with the described threshold:

    @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 );
              }
            }
        );
      }
    }
    
    0 讨论(0)
  • 2021-02-13 02:31

    The exact same answer as @Sergey Grinev. Make sure your version of java is up-to-date (JDK 1.8 or later).

    Platform.runLater(()->myTextField.requestFocus());
    
    0 讨论(0)
  • 2021-02-13 02:41

    The older answers account for the case of Platform.runLater not working, but this answer covers also the case of multiple requests on multiple nodes.

    Problem is: the order in which the scene property becomes non-null for the nodes, and thence the order in which the added listeners get called, is not necessarily the same as the order in which the two listeners were added. And so this order:

    requestFocusOnSceneAvailable(node1)
    requestFocusOnSceneAvailable(node2)
    

    might unexpectedly result in this order:

    node2.requestFocus()
    node1.requestFocus()
    

    A solution requires having the listeners call requestFocus() only on the most recent node, which can be tracked with a static variable:

    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);
                }
            }
        });
    }
    

    Note, this solution assumes there is only one scene.

    0 讨论(0)
提交回复
热议问题