【问题标题】:TextArea loose focus when using scrollBar使用滚动条时 TextArea 失去焦点
【发布时间】:2014-09-30 12:45:44
【问题描述】:

当你创建一个 TextArea 时,你可以监听它的“focusedProperty”。

但如果用户触摸 TextArea 的内部滚动条(如果它太小),则 TextArea 的焦点会丢失(因为滚动条有焦点)。

但就我而言,TextArea 仍然是焦点,因为滚动条是 TextArea 的一部分,甚至无法访问它们。

如何破解 textArea 以便检测用户何时使用滚动条?我想破解/创建一个focusedProperty,它会在用户输入文本或使用滚动条时返回true。

【问题讨论】:

    标签: javafx javafx-8


    【解决方案1】:

    观察ScenefocusOwner属性,并创建一个BooleanBinding,如果它是文本区域的后代,则为true,否则为false:

    import java.util.stream.IntStream;
    
    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.binding.BooleanBinding;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.TextArea;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class TextAreaFocusTest extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            TextArea textArea = new TextArea();
            IntStream.rangeClosed(1, 200).forEach(i -> textArea.appendText(" "));
            IntStream.rangeClosed(1, 80).forEach(i -> textArea.appendText("\nLine "+i));
            Label label = new Label();
            TextField textField = new TextField();
            VBox root = new VBox(10, textArea, textField, label);
            Scene scene = new Scene(root, 400, 400);
    
            BooleanBinding focus = Bindings.createBooleanBinding(() -> {
                for (Node n = scene.getFocusOwner(); n!= null ; n=n.getParent()) {
                    if (n == textArea) return true ;
                }
                return false ;
            }, scene.focusOwnerProperty());
    
            label.textProperty().bind(Bindings.when(focus).then("Focused").otherwise("Not Focused"));
    
            primaryStage.setScene(scene);
            primaryStage.show();        
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 感谢您的回复。可以肯定的是,每次场景焦点所有者更改时都会调用回调,对吗?
    • 每次场景的焦点所有者更改时,都会重新计算BooleanBinding (focus) 的值。 (实际上,这并不完全正确,但实际上是正确的......)如果您使用该BooleanBinding 注册一个侦听器,那么只要它的值更改,就会调用该侦听器。如果您绑定到 BooleanBinding(就像我在示例中对标签的 text 属性所做的那样),则绑定到它的属性将在其值更改时重新计算。
    • 另请参阅我对 James 的回答的详细说明,即使场景发生变化,也可以使这项工作发挥作用,例如在将textArea 添加到场景之前或(不太可能)在场景之间移动textArea 时设置绑定时。
    • @TomasMikula 的回答很好:例如,如果您在控制器的初始化方法中,则文本区域不会被添加到场景中。
    • 好的,谢谢你的精确。事实上,我在它上面放了一个 InvalidationListener 并且它不起作用。但是我放了一个 ChangeListener 然后它起作用了,因为我想要的是返回的值,而不是计算值的时候。感谢 Tomas 的评论,就我而言,场景已经存在,但当它不存在时它肯定会很有用
    【解决方案2】:

    这是@James_D 答案的变体,以防您需要能够从他的答案中获取focus 绑定,而无需参考场景,例如如果您需要在将文本区域添加到场景之前设置绑定,正在实现库,或者只是想让您的代码更少纠缠。

    此解决方案使用EasyBind 库方便地选择嵌套属性(从sceneProperty 中选择focusOwnerProperty)。

    public static Binding<Boolean> containsFocus(Node node) {
        return EasyBind.monadic(node.sceneProperty())
                .flatMap(Scene::focusOwnerProperty)
                .map(owner -> {
                    for (Node n = owner; n != null; n = n.getParent()) {
                        if (n == node) return true ;
                    }
                    return false ;
                })
                .orElse(false); // when node.getScene() is null
    }
    

    【讨论】:

      猜你喜欢
      • 2016-10-30
      • 1970-01-01
      • 2020-06-06
      • 1970-01-01
      • 2012-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多