【问题标题】:Scrolling on VBox with lots of Textfields在带有大量文本字段的 VBox 上滚动
【发布时间】:2020-02-23 17:17:43
【问题描述】:

我在滚动窗格的 VBox 中有很多文本字段。滚动和触摸文本字段时,它只会抓住焦点。所以平滑滚动是不可能的。我怎样才能让它很好地滚动而不会不必要地关注任何文本字段。滚动时是否需要使用文本字段上的事件?

【问题讨论】:

    标签: mobile javafx gluon


    【解决方案1】:

    如果您想要滚动一长串文本字段,而不让它们占据焦点,直到您完全停止滚动,并且显然想要选择其中一个文本字段,这是一种可能的解决方案。

    它基于自定义的“按住”事件,灵感来自 question

    想法是将TextField控件捆绑在一个HBox中,并使用容器的鼠标透明属性禁用对控件的访问。

    然后,每当您点击容器时,如果您按下足够长的时间,容器将允许访问控件并显示键盘。否则,您将继续滚动,但不会显示键盘。

    我将仅在 iOS 上使用此 question 中提到的 KeyboardService

    public class BasicView extends View {
    
        public BasicView(String name) {
            super(name);
    
            setTop(new Button("Button"));
    
            VBox controls = new VBox(15.0);
    
            controls.setAlignment(Pos.CENTER);
    
            ScrollPane pane = new ScrollPane(controls);
            controls.prefWidthProperty().bind(pane.widthProperty().subtract(20));
    
            for (int i = 0; i < 100; i++) {
                final Label label = new Label("TextField " + (i + 1));
                final TextField textField1 = new TextField();
                HBox.setHgrow(textField1, Priority.ALWAYS);
                HBox box = new HBox(10, label, textField1);
                box.setMouseTransparent(true);
    
                box.setAlignment(Pos.CENTER_LEFT);
                box.setPadding(new Insets(5));
                controls.getChildren().add(box);
            }
    
            addPressAndHoldHandler(controls, Duration.millis(300), eStart -> {
                    for (Node box : controls.getChildren()) {
                        box.setMouseTransparent(true);
                    }
                }, eEnd -> {
                    for (Node box : controls.getChildren()) {
                        if (box.localToScene(box.getBoundsInLocal()).contains(eEnd.getSceneX(), eEnd.getSceneY())) {
                            box.setMouseTransparent(false);
                            ((HBox) box).getChildren().get(1).requestFocus();
                            break;
                        }
                    }
                });
            setCenter(pane);
    
            // iOS only
            Services.get(KeyboardService.class).ifPresent(keyboard -> {
                keyboard.visibleHeightProperty().addListener((obs, ov, nv) -> {
                    if (nv.doubleValue() > 0) {
                        for (Node box : controls.getChildren()) {
                            Node n1 = ((HBox) box).getChildren().get(1);
                            if (n1.isFocused()) {
                                double h = getScene().getHeight() - n1.localToScene(n1.getBoundsInLocal()).getMaxY();
                                setTranslateY(-nv.doubleValue() + h);
                                break;
                            }
                        }
                    } else {
                        setTranslateY(0);
                    }
                });
            });
        }
    
        @Override
        protected void updateAppBar(AppBar appBar) {
            appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
            appBar.setTitleText("Scrolling over TextFields");
        }
    
        private void addPressAndHoldHandler(Node node, Duration holdTime, 
                EventHandler<MouseEvent> handlerStart, EventHandler<MouseEvent> handlerEnd) {
            class Wrapper<T> { 
                T content; 
            }
    
            Wrapper<MouseEvent> eventWrapper = new Wrapper<>();
    
            PauseTransition holdTimer = new PauseTransition(holdTime);
            holdTimer.setOnFinished(event -> handlerEnd.handle(eventWrapper.content));
    
            node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
                handlerStart.handle(event);
                eventWrapper.content = event;
                holdTimer.playFromStart();
            });
            node.addEventHandler(MouseEvent.MOUSE_RELEASED, event -> holdTimer.stop());
            node.addEventHandler(MouseEvent.DRAG_DETECTED, event -> holdTimer.stop());
        }
    }
    

    请注意,我在顶部添加了一个按钮,以便在您显示视图时将焦点放在首位。

    当您单击/点击controls VBox 时,它会将所有框鼠标设置为透明:box.setMouseTransparent(true);,并启动PauseTransition

    如果在 300 毫秒之前释放鼠标或拖动鼠标(这可以在您方便时进行更改),则过渡将停止。否则,在 300 毫秒后,它会将框设置为box.setMouseTransparent(false);,并将焦点设置在 TextField 上,此时键盘将出现。

    【讨论】:

      【解决方案2】:

      这是我用于您描述的目的的一个类:

      public class MouseClickedFilter{
      
          private final Node                       observableNode;
      
          private BooleanProperty                  scrolling          = new ReadOnlyBooleanWrapper(false);
      
          private EventHandler<? super MouseEvent> dragDetectedFilter = e -> scrolling.set(true);
          private EventHandler<? super MouseEvent> mouseExitedHandler = e -> scrolling.set(false);
      
          private EventHandler<? super MouseEvent> mouseClickedFilter = evt ->
                                                                          {
                                                                              if (scrolling.get()) {
                                                                                  evt.consume();
                                                                                  scrolling.set(false);
                                                                              }
                                                                          };
      
          private boolean                          listenersEnabled;
      
          public MouseClickedFilter(Node observableNode) {
              this.observableNode = observableNode;
          }
      
          public void activate() {
              if (!listenersEnabled) {
                  observableNode.addEventFilter(MouseEvent.DRAG_DETECTED, dragDetectedFilter);
                  observableNode.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler);
                  observableNode.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseClickedFilter);
              }
          }
      
          public void deactivate() {
              if (listenersEnabled) {
                  observableNode.removeEventFilter(MouseEvent.DRAG_DETECTED, dragDetectedFilter);
                  observableNode.removeEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler);
                  observableNode.removeEventFilter(MouseEvent.MOUSE_CLICKED, mouseClickedFilter);
              }
          }
      
          public final ReadOnlyBooleanProperty scrollingProperty() {
              return scrolling;
          }
      
          public final boolean isScrolling() {
              return scrolling.get();
          }
      
      }
      

      ObservableNode 是您的 ScrollPane 包含文本字段

      【讨论】:

      • 感谢您的代码,但它没有帮助。滚动仍然卡住并显示 TextFields。
      • 是的,滚动卡住了。这似乎是 Android 上 ScrollPane 与 javafxport 结合的一个基本问题,无论 ScrollPane 中使用的节点如何。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-29
      • 2018-05-02
      • 1970-01-01
      • 2013-09-24
      • 1970-01-01
      相关资源
      最近更新 更多