首页 > 解决方案 > JavaFX中如何让Canvas填满BorderPane的中心?

问题描述

到目前为止,这是我的代码(在 SceneBuilder 中完成)。

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <top>
      <MenuBar BorderPane.alignment="CENTER">
        <menus>
          <Menu mnemonicParsing="false" text="File">
            <items>
              <MenuItem mnemonicParsing="false" text="Close" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Edit">
            <items>
              <MenuItem mnemonicParsing="false" text="Delete" />
            </items>
          </Menu>
          <Menu mnemonicParsing="false" text="Help">
            <items>
              <MenuItem mnemonicParsing="false" text="About" />
            </items>
          </Menu>
        </menus>
      </MenuBar>
   </top>
   <center>
      <Canvas fx:id="canvas" onMouseClicked="#createNode" BorderPane.alignment="CENTER" />
   </center>
   <right>
      <ScrollPane fitToWidth="true" hbarPolicy="NEVER" minViewportWidth="138.0" minWidth="-Infinity" prefHeight="375.0" prefViewportWidth="138.0" prefWidth="138.0" BorderPane.alignment="CENTER">
         <content>
            <VBox alignment="TOP_CENTER" minWidth="-Infinity" prefHeight="140.0" prefWidth="133.0">
               <children>
                  <Label text="Graph Type" underline="true">
                     <VBox.margin>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </VBox.margin>
                  </Label>
                  <RadioButton fx:id="directedButton" mnemonicParsing="false" selected="true" text="Directed">
                     <VBox.margin>
                        <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                     </VBox.margin>
                     <toggleGroup>
                        <ToggleGroup fx:id="graphType" />
                     </toggleGroup>
                  </RadioButton>
                  <RadioButton fx:id="undirectedButton" mnemonicParsing="false" text="Undirected" toggleGroup="$graphType">
                     <VBox.margin>
                        <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                     </VBox.margin>
                  </RadioButton>
                  <Label alignment="TOP_CENTER" text="Diagram Tools" underline="true">
                     <VBox.margin>
                        <Insets />
                     </VBox.margin>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <ToggleButton fx:id="selectButton" mnemonicParsing="false" selected="true" text="Select">
                     <toggleGroup>
                        <ToggleGroup fx:id="diagramTools" />
                     </toggleGroup>
                     <VBox.margin>
                        <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                     </VBox.margin>
                  </ToggleButton>
                  <ToggleButton fx:id="nodeButton" mnemonicParsing="false" text="Node" toggleGroup="$diagramTools">
                     <VBox.margin>
                        <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                     </VBox.margin>
                  </ToggleButton>
                  <ToggleButton fx:id="edgeButton" mnemonicParsing="false" text="Edge" toggleGroup="$diagramTools">
                     <VBox.margin>
                        <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                     </VBox.margin>
                  </ToggleButton>
                  <Label text="Diagram Actions" underline="true">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <VBox alignment="TOP_CENTER" prefHeight="151.0" prefWidth="120.0">
                     <children>
                        <Button mnemonicParsing="false" text="DFS">
                           <VBox.margin>
                              <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                           </VBox.margin>
                        </Button>
                        <Button layoutX="52.0" layoutY="41.0" mnemonicParsing="false" text="BFS">
                           <VBox.margin>
                              <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                           </VBox.margin>
                        </Button>
                        <Button layoutX="53.0" layoutY="73.0" mnemonicParsing="false" text="Shortest Path">
                           <VBox.margin>
                              <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                           </VBox.margin>
                        </Button>
                        <Button layoutX="27.0" layoutY="102.0" mnemonicParsing="false" text="Hamilton Path" />
                        <Button layoutX="24.0" layoutY="134.0" mnemonicParsing="false" text="Hamilton Cycle">
                           <VBox.margin>
                              <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                           </VBox.margin>
                        </Button>
                        <Button layoutX="22.0" layoutY="163.0" mnemonicParsing="false" text="Eulerian Path">
                           <VBox.margin>
                              <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                           </VBox.margin>
                        </Button>
                        <Button layoutX="27.0" layoutY="191.0" mnemonicParsing="false" text="Eulerian Cycle">
                           <VBox.margin>
                              <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                           </VBox.margin>
                        </Button>
                     </children>
                  </VBox>
               </children>
            </VBox>
         </content>
      </ScrollPane>
   </right>
   <left>
      <VBox prefHeight="375.0" prefWidth="119.0" style="-fx-background-color: black;" BorderPane.alignment="CENTER" />
   </left>
</BorderPane>

我在右侧有一个由 ScrollPane 制成的工具栏,当窗口缩小时,工具栏在窗口中保持可见。顶部和左侧将被占用,底部为空。我希望中心是一个随着窗口大小变化而缩小和增长的画布,以填充其余空间。但在 SceneBuilder 中,我只能设置固定的宽度/高度。

无论窗口大小如何,我怎样才能让 BorderPane 的中心画布自动填充任何未占用的空间?可以在 SceneBuilder 中完成,还是只能在我的 Controller 或 FXML 文件本身中完成?

编辑:这是我的主要课程,以尽量减少可重复的示例,尽管它只是设置它。控制器类Controller本质上是空的。

public class Main extends Application {


    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception{

        Parent root = FXMLLoader.load(getClass().getResource("sample1.fxml"));
        stage.setTitle("Sample 2");
        stage.setScene(new Scene(root, 500, 500));
        stage.show();
    }
}

标签: javajavafxscenebuilder

解决方案


ACanvas不是可调整大小的节点,因此默认情况下不会调整大小。您必须安排setWidth()并被setHeight()调用(当您这样做时,您可能希望重新绘制画布)。

一种选择是定义一个自定义区域来保存画布,并覆盖其layoutChildren()方法来调整画布大小和重新绘制画布。这可能看起来像:

public class CanvasPane extends Region {
    private final Canvas canvas ;
    private Consumer<Canvas> repaint ;
    public CanvasPane() {
        this.canvas = new Canvas() ;
        getChildren().add(canvas);
        repaint = c -> {} ;
    }
    
    public Consumer<Canvas> getRepaint() {
        return repaint;
    }
    
    public void setRepaint(Consumer<Canvas> repaint) {
        this.repaint = repaint ;
    }

    public Canvas getCanvas() {
        return canvas ;
    }
    
    @Override
    protected void layoutChildren() {
        super.layoutChildren();
        double width = getWidth();
        canvas.setWidth(width);
        double height = getHeight();
        canvas.setHeight(height);
        repaint.accept(canvas);
    }
}

现在在 FXML 中:

<BorderPane>
    <center>
        <CanvasPane fx:id="canvasPane" />
    </center>
</BorderPane>

在控制器中:

public class Controller {

    @FXML
    private CanvasPane canvasPane ;

    public void initialize() {
        canvasPane.setRepaint(this::repaintCanvas);
    }

    private void repaintCanvas(Canvas canvas) {
        // example canvas painting:
        double width = canvas.getWidth() ;
        double height = canvas.getHeight() ;
        GraphicsContext graphics = canvas.getGraphicsContext2D();
        graphics.setFill(Color.web("#868c8c"));
        graphics.fillRect(0, 0, width, height);
        graphics.setFill(Color.web("#08cd4f"));
        graphics.fillRect(20, 20, width-40, height-40);
    }
}

现在,当边框窗格调整大小时,它将为CanvasPane(即 a Region)分配适当的空间。该layoutChildren()方法将被自动调用,并且画布将被调整为其区域的大小(并重新绘制,如果您repaint在控制器中提供 a)。


推荐阅读