JavaFX 2 BorderPane Use Full Space

2020-07-10 05:26发布

I´m just facing a little issue that I can´t solve by myself. I try to place a vBox including a TextField and a HTML-Editor in my BorderPane, but the full space is not used. Another problem is that if I shrink the window, the html-editor overlaps with my left option window.

enter image description here

private void initEditor()
{
editor = new HTMLEditor();
editor.setId("editor");
editor.lookup(".top-toolbar").setDisable(true);
editor.lookup(".top-toolbar").setManaged(false);
((ToolBar) editor.lookup(".bottom-toolbar")).getItems().addAll(FXCollections.observableArrayList(((ToolBar)editor.lookup(".top-toolbar")).getItems()));

editorBox = new VBox();
TextField field = new TextField();
field.setPrefHeight(36);
field.setId("editor-title");
editorBox.setFillWidth(true);
editorBox.getChildren().addAll(field, editor);
    root.setCenter(editorBox);
}

4条回答
Anthone
2楼-- · 2020-07-10 05:34

How to set and resize ScrollPane with FXML:

package anchorscroll;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;

public class FXMLDocumentController implements Initializable {

    private Label label;
    @FXML
    private VBox vp;
    @FXML
    private ScrollPane sp;
    @FXML

    private Pane panel;
    @FXML
    private double Width;
    @FXML
    private double Height;

    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
        Width = panel.getWidth();
        Height = panel.getHeight();
        sp.setPrefWidth(Width - 60);
        sp.setPrefHeight(Height - 60);

        panel.widthProperty().addListener(p -> {
            Width = panel.getWidth();
            Height = panel.getHeight();
            System.out.println("Width " + Width + " Height " + Height);
            sp.setPrefWidth(Width - 60);
            sp.setPrefHeight(Height - 60);
        });

        panel.heightProperty().addListener(p -> {
            Width = panel.getWidth();
            Height = panel.getHeight();
            System.out.println("Width " + Width + " Height " + Height);
            sp.setPrefWidth(Width - 60);
            sp.setPrefHeight(Height - 60);
        });

        VBox v = new VBox();
        for (int i = 0; i < 100; i++) {
            v.getChildren().add(new Button("Hello " + i));
        }
        sp.setContent(v);

        VBox p = new VBox();
        try {
            p = FXMLLoader.load(getClass().getResource("NewMessage.fxml")); // "/fxml/Register.fxml"
            sp.setContent(p);
        } catch (IOException ex) {
            Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

}

!!! NewMessage.fxml need start with VBox panel !!!

Comment or delete this 2 lines if u don't need load FXML:

p = FXMLLoader.load(getClass().getResource("NewMessage.fxml"));
sp.setContent(p);

And layout:

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="panel" prefHeight="400.0" prefWidth="600.0" stylesheets="@Styles.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="anchorscroll.FXMLDocumentController">
    <children>
        <HBox prefHeight="60.0" prefWidth="600.0">
            <children>
                <Label text="Label" />
            </children>
        </HBox>
        <VBox fx:id="vp" layoutY="60.0" prefHeight="540.0" prefWidth="60.0">
            <children>
                <Button mnemonicParsing="false" prefWidth="50.0" text="Button" />
            </children>
        </VBox>
        <ScrollPane fx:id="sp" fitToHeight="true" fitToWidth="true" layoutX="60.0" layoutY="60.0" prefHeight="340.0" prefWidth="540.0" vbarPolicy="ALWAYS" />
    </children>
</Pane>

Layout image (topBar, leftBar, scrollContentPane)

查看更多
霸刀☆藐视天下
3楼-- · 2020-07-10 05:44
    GridPane gridPane = (GridPane) editor.getChildrenUnmodifiable().get(0);
    RowConstraints row1 = new RowConstraints();
    row1.setVgrow(Priority.NEVER);
    RowConstraints row2 = new RowConstraints();
    row2.setVgrow(Priority.NEVER);
    RowConstraints row3 = new RowConstraints();
    row3.setVgrow(Priority.ALWAYS);
    gridPane.getRowConstraints().addAll(row1, row2, row3);
查看更多
叛逆
4楼-- · 2020-07-10 05:58

OK, so a few things going wrong here, I'll try to address them provide some advice and a sample solution.

I try to place a vBox including a TextField and a HTML-Editor in my BorderPane, but the full space is not used.

You need to use the VBox.setVgrow(editor, Priority.ALWAYS) method to get the HTMLEditor to take up any extra space in the VBox. Also, make sure that the HTMLeditor has an unbounded max height so that it can grow to fit the available area, for example editor.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE). Calling editorBox.setFillWidth(true) is redundant as the default value for the fillWidth property is true.

But even if you do all that, (as of 2.2b13) there is a bug in WebPane sizing which will cause you problems. Internally the WebPane is implemented as a GridPane containing the Toolbar and an editable WebView. By default, WebView has a preferred size of 800x600. The HtmlEditor control does not set the GridPane constraints for the WebView to allow it to be sized past it's preferred size.

To work around this, you can lookup the WebView via a css lookup after it has been added to an active Scene and adjust the GridPane constraints manually. Here is some code to do that:

stage.show();
...
WebView webview = (WebView) editor.lookup("WebView");
GridPane.setHgrow(webview, Priority.ALWAYS);
GridPane.setVgrow(webview, Priority.ALWAYS);

I shrink the window, the html-editor overlaps with my left option window.

Explicitly set the minimum width setting for the center pane in your BorderPane so that it won't overflow over the outer edge Panes.

editorBox.setMinSize(0, 0);

You need to do this because the BorderPane documentation states:

BorderPane does not clip its content by default, so it is possible that childrens' bounds may extend outside its own bounds if a child's min size prevents it from being fit within it space.


As an aside the lookup calls in your code are suspicious. Normally you can't lookup nodes by css ID until the node has been added to an active scene on a displayed stage and the JavaFX system has had a chance to run a CSS layout pass on the node - otherwise the lookup will just return null.


For debugging JavaFX layout issues, the ScenicView application is invaluable.


Here is a complete example to generate a layout similar to the image linked in your question, but without the issues you mention.

import com.javafx.experiments.scenicview.ScenicView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.*;
import javafx.stage.*;

public class HtmlEditorInBorderPane extends Application {
  public static void main(String[] args) { launch(args); }
  @Override public void start(final Stage stage) throws IOException {
    // option pane.
    VBox optionPane = new VBox(10);
    MenuBar menuBar = new MenuBar();
    menuBar.getMenus().addAll(new Menu("School"), new Menu("Social"), new Menu("Network"));
    TreeItem<String> root = new TreeItem<>("Private Notes");
    root.setExpanded(false);
    root.getChildren().addAll(new TreeItem<>("Layout"), new TreeItem<>("is not"), new TreeItem<>("easy"));
    TreeView<String> notes = new TreeView<>(root);
    optionPane.getChildren().addAll(menuBar, new Label("Kaiser Notes"), notes);
    optionPane.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");

    // editor pane.
    HTMLEditor editor = new HTMLEditor();
    VBox editorBox = new VBox(10);
    TextField textField = new TextField();
    editorBox.getChildren().addAll(textField, editor);
    editorBox.setStyle("-fx-background-color: mistyrose; -fx-padding: 10;");
    editor.setHtmlText(getSampleText());

    // option layout constraints
    VBox.setVgrow(notes, Priority.ALWAYS);

    // editor layout constraints
    textField.setMinHeight(Control.USE_PREF_SIZE); // stop the textfield from getting squashed when the scene is sized small.
    VBox.setVgrow(editor, Priority.ALWAYS);        // tells the editor to fill available vertical space.
    editorBox.setMinSize(0, 0);                    // stops the editor from overflowing it's bounds in a BorderPane.

    // layout the scene.
    BorderPane layout = new BorderPane();
    layout.setLeft(optionPane);
    layout.setCenter(editorBox);
    stage.setScene(new Scene(layout));
    stage.show();

    // addition of static layout grow constraints for the htmleditor embedded webview.
    WebView webview = (WebView) editor.lookup("WebView");
    GridPane.setHgrow(webview, Priority.ALWAYS);  // allow the webview to grow beyond it's preferred width of 800.
    GridPane.setVgrow(webview, Priority.ALWAYS);  // allow the webview to grow beyond it's preferred height of 600.
  }

  // get some dummy lorem ipsum sample text.
  private String getSampleText() throws IOException {
    StringBuilder builder = new StringBuilder();
    try (BufferedReader in = new BufferedReader(new InputStreamReader(new URL("http://www.lorem-ipsum-text.com/").openStream()))) {
      String string;
      while ((string = in.readLine()) != null) {
        builder.append(string);
      }
    }
    return builder.toString();
  }
}
查看更多
叛逆
5楼-- · 2020-07-10 05:58

I was also facing the same kind of issue. My anchorPane inside borderPane was not filling up. Setting a large number for prefSize worked for me. So when border pane grows, it tries to provide as much size as possible to its child

anchorPane.setMinSize(25, 25);
anchorPane.setPrefSize(25000, 25000);
查看更多
登录 后发表回答