【问题标题】:Set divider position of SplitPane设置 SplitPane 的分隔线位置
【发布时间】:2016-03-29 16:58:43
【问题描述】:

我想将 SplitPane 的分隔线设置为某个默认位置。这不起作用,分隔线停留在中间:

public void start(Stage primaryStage) throws Exception{

    SplitPane splitPane = new SplitPane(new Pane(), new Pane());

    // Report changes to the divider position
    splitPane.getDividers().get(0).positionProperty().addListener(
            o -> System.out.println(splitPane.getDividerPositions()[0])
    );

    // Doesn't work:
    splitPane.setDividerPositions(0.8);

    // The docs seem to recommend the following (with floats instead of
    // doubles, and with one number more than there are dividers, which is
    // weird), but it doesn't work either:
    //splitPane.setDividerPositions(0.8f, 0.2f);

    primaryStage.setScene(new Scene(splitPane));
    primaryStage.setMaximized(true);
    primaryStage.show();
}

输出:

0.8
0.5

这表明某些东西将它重置到中间。

我怎样才能做到这一点?

【问题讨论】:

    标签: javafx javafx-8


    【解决方案1】:

    问题似乎是在Stage 最大化期间设置SplitPane 宽度时,分隔线位置被重置。之后通过监听窗口的显示属性来设置分隔线位置,如下所示:

    primaryStage.showingProperty().addListener(new ChangeListener<Boolean>() {
    
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
            if (newValue) {
                splitPane.setDividerPositions(0.8);
                observable.removeListener(this);
            }
        }
    });
    

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题,上面的解决方案对我来说不能可靠地工作。 所以我为 SplitPane 创建了一个自定义皮肤:

      public class DumbSplitPaneSkin extends SplitPaneSkin {
      
        public DumbSplitPaneSkin(SplitPane splitPane) {
          super(splitPane);
        }
      
        @Override
        protected void layoutChildren(double x, double y, double w, double h) {
          double[] dividerPositions = getSkinnable().getDividerPositions();
          super.layoutChildren(x, y, w, h);
          getSkinnable().setDividerPositions(dividerPositions);
        }
      }
      

      可以通过 css 或重写 SplitPane.createDefaultSkin() 来使用此皮肤。您还可以以编程方式设置为 splitPane.setSkin(new DumbSplitPaneSkin(splitPane));

      【讨论】:

      • 这正是我所期望的 SplitPane 一开始的表现......有时“聪明”并不总是好的。
      【解决方案3】:

      Stage 初始化期间,窗口大小会发生多次变化,直到布局完成。每次更改都会修改分隔线的位置。如果你想控制分隔线的位置,它们必须在 Stage 完全初始化后设置:

      private boolean m_stageShowing = false;
      
      @Override
      public void start(Stage primaryStage) throws Exception {
          SplitPane splitPane = new SplitPane(new Pane(), new Pane());
      
          ChangeListener<Number> changeListener = new ChangeListener<Number>() {
              @Override
              public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                  splitPane.setDividerPositions(0.8);
                  if (m_stageShowing) {
                      observable.removeListener(this);
                  }
              }
          };
          splitPane.widthProperty().addListener(changeListener);
          splitPane.heightProperty().addListener(changeListener);
      
          primaryStage.setScene(new Scene(splitPane));
          primaryStage.setMaximized(true);
          primaryStage.show();
          m_stageShowing = true;
      }
      

      【讨论】:

        【解决方案4】:

        正如其他人指出的那样,问题是当舞台最大化时分隔线位置被重置。

        您可以通过将ResizableWithParent 设置为false 来防止这种情况发生。

        示例

        假设你有一个SplitPane,里面有两个嵌套的容器。这是 fxml 提取:

        <SplitPane fx:id="splitPane" dividerPositions="0.25">
                <VBox fx:id="leftSplitPaneContainer" />
                <FlowPane fx:id="rightSplitPaneContainer"/>
        </SplitPane>
        

        这是控制器类的摘录:

        @FXML
        private SplitPane splitPane;
        @FXML
        private VBox leftSplitPaneContainer;
        @FXML
        private FlowPane rightSplitPaneContainer;
        

        然后您只需在两个容器上调用SplitPane.setResizableWithParent() 即可防止重置分隔符位置:

        public void initialize(){
                SplitPane.setResizableWithParent(leftSplitPaneContainer, false);
                SplitPane.setResizableWithParent(rightSplitPaneContainer, false);
            }
        

        即使您最大化窗口,分隔线位置现在仍将保持在 0.25。

        不涉及复杂的侦听器或覆盖SplitPaneSkin

        【讨论】:

          【解决方案5】:

          您可以将电话 setDividerPositions 包装为

          Platform.runLater(new Runnable() { @覆盖 公共无效运行(){ splitPane.setDividerPositions(0.8); } });

          这不是 100% 可靠的解决方案,因为 run() 方法在未指定的时间在 JFX 线程中执行,但它适用于简单的初始化情况。

          【讨论】:

            【解决方案6】:

            这是我的结果:

            import javafx.application.Application;
            import javafx.event.ActionEvent;
            import javafx.event.EventHandler;
            import javafx.geometry.Orientation;
            import javafx.scene.*;
            import javafx.scene.control.*;
            import javafx.scene.layout.*;
            import javafx.scene.paint.Color;
            import javafx.stage.*;
            
            /**
             * SplitPane, Dialogbox example
             * @author Pataki István
             */
            public class SimpleDocking extends Application {
            
              private double splitPosition = 0;
              private SplitPane rootPane = new SplitPane();
              private MyDialog dialog;
              private BorderPane dockedArea;
            
              @Override
              public void start(final Stage stage) throws Exception {
                rootPane.setOrientation(Orientation.VERTICAL);
                rootPane.setBorder(new Border(new BorderStroke(
                                                Color.GREEN,
                                                BorderStrokeStyle.SOLID,
                                                new CornerRadii(5),
                                                new BorderWidths(3))
                ));
            
                dockedArea = new BorderPane(new TextArea("Some docked content"));
                final FlowPane centerArea = new FlowPane();
                final Button undockButton = new Button("Undock");
                centerArea.getChildren().add(undockButton);
                rootPane.getItems().addAll(centerArea, dockedArea);
                stage.setScene(new Scene(rootPane, 300, 300));
                stage.show();
            
                dialog = new MyDialog(stage);
                undockButton.disableProperty().bind(dialog.showingProperty());
                undockButton.setOnAction(new EventHandler<ActionEvent>() {
                  @Override
                  public void handle(ActionEvent actionEvent) {
                    handler(stage);
                  }
                });
              }
            
              private void handler(Stage stage) {
                splitPosition = rootPane.getDividerPositions()[0];
                rootPane.getItems().remove(dockedArea);
                dialog.setOnHidden(windowEvent -> {
                  rootPane.getItems().add(dockedArea);
                  rootPane.setDividerPositions(splitPosition);
                });
                dialog.setContent(dockedArea);
                dialog.show(stage);
              }
            
              private class MyDialog extends Popup {
                private BorderPane root;
            
                private MyDialog(Window parent) {
                  root = new BorderPane();
                  root.setPrefSize(200, 200);
                  root.setStyle("-fx-border-width: 1; -fx-border-color: gray");
                  root.setTop(buildTitleBar());
                  setX(parent.getX() + 50);
                  setY(parent.getY() + 50);
                  getContent().add(root);
                }
            
                public void setContent(Node content) {
                  root.setCenter(content);
                }
            
                private Node buildTitleBar() {
                  BorderPane pane = new BorderPane();
                  pane.setStyle("-fx-background-color: burlywood; -fx-padding: 5");
                  final Delta dragDelta = new Delta();
            
                  pane.setOnMousePressed(mouseEvent -> {
                    dragDelta.x = getX() - mouseEvent.getScreenX();
                    dragDelta.y = getY() - mouseEvent.getScreenY();
                  });
            
                  pane.setOnMouseDragged(mouseEvent -> {
                    setX(mouseEvent.getScreenX() + dragDelta.x);
                    setY(mouseEvent.getScreenY() + dragDelta.y);
                  });
            
                  Label title = new Label("My Dialog");
                  title.setStyle("-fx-text-fill: midnightblue;");
                  pane.setLeft(title);
            
                  Button closeButton = new Button("X");
                  closeButton.setOnAction(actionEvent -> hide());
                  pane.setRight(closeButton);
                  return pane;
                }
              }
            
              private static class Delta {
                double x, y;
              }
            
              public static void main(String[] args) throws Exception {
                launch();
              }
            }
            

            一切如你所愿。

            【讨论】:

              【解决方案7】:

              因为您通常在拆分窗格中至少有一些东西,例如。 vbox,只需设置最小和最大宽度,它会自动设置分隔符。

              【讨论】:

                【解决方案8】:

                试试

                splitPane.setDividerPosition(0, percentage);
                

                参数为setDividerPosition(int dividerIndex, double percent)

                【讨论】:

                • 这与我发布的内容基本相同,并产生完全相同的结果。它是一个介于 0.0 和 1.0 之间的值,而不是真正的百分比。
                • 是的。这可能是操作顺序的事情。尝试添加一个按钮并将方法放在那里。
                • 太好了,行得通!但它必须在用户交互之前存在。叹。除了设置一个丑陋的计时器之外还有什么想法吗?
                • 执行 setDividerPosition after primaryStage.show() 似乎有效。有时。
                猜你喜欢
                • 2012-08-14
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-06-05
                相关资源
                最近更新 更多