【问题标题】:JavaFX Canvas not resizing correctly inside StackPaneJavaFX Canvas 在 StackPane 中无法正确调整大小
【发布时间】:2017-01-16 22:48:57
【问题描述】:

下面是Main.classController.class,我尽量让它更简单。

程序输出以下窗口:


黑色是StackPane,名为visualizerStackPane,里面是Canvas,名为visualizerCanvas我想要做的是 StackPane 内的 Canvas 相应地调整为 StackPane 大小减法 4 。但是,当您调整窗口大小时,一切都会失败。

你必须尝试什么......

1)最大化窗口,然后将其归一化。

2)增大窗口大小,然后慢慢减小。

为什么会这样?我有这个问题 3 个月了,但我找不到解决方案...

我也看过JavaFX - Resize Canvas when screen is resized,但这里的问题似乎有所不同...


Main.java:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {

            //Scene
            Scene scene = new Scene(new Controller(), 400, 400);
            primaryStage.setScene(scene);

            //Show
            primaryStage.show();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

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

Controller.java:

我已经修改了 Controller 类来绘制 Canvas 并从 Initialize 方法中删除了绑定,它不起作用它会产生以下内容:

如果我不手动设置画布的宽度和高度,它甚至不会被绘制...我不知道为什么。

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;

public class Controller extends StackPane {

    @FXML
    private GridPane container;

    @FXML
    private GridPane topGridPane;

    @FXML
    private StackPane visualizerStackPane;

    @FXML
    private Canvas visualizerCanvas;

    @FXML
    private VBox topRightVBox;

    @FXML
    private StackPane mediaFileStackPane;

    @FXML
    private GridPane bottomGridPane;

    // ------------------------------

    GraphicsContext gc;
    int             canvasWidth     = 0;
    int             canvasHeight    = 0;

    /**
     * Constructor
     */
    public Controller() {

        // ------------------------FXMLLoader ---------------------------------
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Controller.fxml"));
        loader.setController(this);
        loader.setRoot(this);

        try {
            loader.load();
        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Controller FXML can't be loaded!", ex);
        }
    }

    /**
     * Called as soon as FXML FILE has been loaded
     */
    @FXML
    private void initialize() {

        gc = visualizerCanvas.getGraphicsContext2D();

        // // VisualizerStackPane inside which visualizerCanvas is
        // visualizerCanvas.widthProperty().bind(visualizerStackPane.widthProperty().subtract(4));
        // visualizerCanvas.heightProperty().bind(visualizerStackPane.heightProperty().subtract(4));
        //

        // Add width and height change listeners
        visualizerStackPane.widthProperty().addListener((observable, oldValue,
                newValue) -> resizeVisualizerCanvas(newValue.doubleValue(), visualizerStackPane.getHeight()));

        visualizerStackPane.heightProperty().addListener((observable, oldValue,
                newValue) -> resizeVisualizerCanvas(visualizerStackPane.getWidth(), newValue.doubleValue())

        );

        repaintCanvas();
    }

    /**
     * Repaints the Canvas
     */
    public void repaintCanvas() {

        //Clear the previous rectangle
        gc.clearRect(0, 0, canvasWidth, canvasHeight);

        // Draw the
        gc.setFill(Color.RED);
        gc.fillRect(0, 0, canvasWidth, canvasHeight);

        // Draw the Line
        gc.setLineWidth(3);
        gc.setStroke(Color.WHITE);
        gc.strokeLine(0, canvasHeight, canvasWidth, 0);

    }

    /**
     * Resizes the visualizerCanvas to the given values.
     *
     * @param width
     *            the width
     * @param height
     *            the height
     */
    public void resizeVisualizerCanvas(double width, double height) {
        if (width > 0 && height > 0) {

            this.canvasWidth = (int) width;
            this.canvasHeight = (int) height;

            // Print something
            System.out.println("Canvas Width is:" + width+" , Canvas Height is:"+height +" Canvas is Resizable: "+visualizerCanvas.isResizable());

            // Repaint the Canvas
            repaintCanvas();

            // ---------------------------Below i have tried several things to
            // solve the problem....

            // Set the width and height of Canvas
            visualizerCanvas.setWidth(width);
            visualizerCanvas.setHeight(height);

            // Careful with contentBias here
            prefWidth(-1);
            prefHeight(-1);

            // autosize()

            // System.out.println("Content Bias is:"+this.getContentBias())

        }
    }
}

Controller.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Region?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>

<fx:root prefHeight="212.0" prefWidth="456.0" type="StackPane" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
   <padding>
      <Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
   </padding>
   <children>
      <GridPane fx:id="container">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="50.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="50.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" percentHeight="40.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" percentHeight="60.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <GridPane fx:id="topGridPane" gridLinesVisible="true" GridPane.columnSpan="2">
              <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="50.0" prefWidth="100.0" />
                <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="50.0" prefWidth="100.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              </rowConstraints>
               <children>
                  <StackPane fx:id="visualizerStackPane" style="-fx-border-color: white; -fx-border-width: 1.5; -fx-background-color: rgb(0,0,0,0.95);">
                     <children>
                        <Canvas fx:id="visualizerCanvas" />
                     </children>
                  </StackPane>
                  <VBox fx:id="topRightVBox" GridPane.columnIndex="1">
                     <children>
                        <StackPane prefHeight="150.0" prefWidth="200.0">
                           <children>
                              <Region style="-fx-background-color: rgb(255,255,255,0.7);" />
                              <Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-font-weight: bold;" text="text here" />
                           </children>
                        </StackPane>
                        <StackPane fx:id="mediaFileStackPane" maxWidth="1.7976931348623157E308" />
                     </children>
                  </VBox>
               </children>
            </GridPane>
            <GridPane fx:id="bottomGridPane" gridLinesVisible="true" GridPane.columnSpan="2" GridPane.rowIndex="1">
              <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="20.0" prefWidth="35.0" />
                <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" percentWidth="60.0" prefWidth="343.0" />
                  <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="20.0" prefWidth="52.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              </rowConstraints>
               <children>
                  <StackPane prefHeight="150.0" prefWidth="200.0" GridPane.columnIndex="2">
                     <children>
                        <Region style="-fx-background-color: rgb(255,255,255,0.7);" />
                        <Label alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" style="-fx-font-weight: bold;" text="text here" textAlignment="CENTER" wrapText="true" StackPane.alignment="CENTER" />
                     </children>
                  </StackPane>
                  <VBox prefHeight="200.0" prefWidth="100.0" spacing="5.0" />
               </children>
            </GridPane>
         </children>
      </GridPane>
   </children>
</fx:root>

最后:

非常感谢能够解决这个问题的人:)。

【问题讨论】:

  • initialize() 方法中删除所有行。您正试图自己控制布局操作,这会干扰 StackPane 的工作能力。相反,您可以在 StackPane 上设置一个宽度为 2 的附加边框,或者将 StackPane 的 padding 设置为所有边 0.5,或者将 Canvas 的 StackPane.margin 设置为所有边的 0.5。
  • @VGR 那么我如何获得画布的宽度和高度?画布会自动调整大小吗?每当我将 ChangeListener 添加到 CanvaswidthPropertyheightProperty 时,我都会得到 -1 。感谢您的帮助... :)
  • StackPane 已经根据 StackPane 的内容大小(StackPane 的大小减去其 insets)调整每个子项的大小。您无需执行任何操作。
  • @VGR 那么我不知道如何在调整大小时获得 Canvas 的宽度和高度,因为属性确实返回 -1 :(
  • 我按照@VGR 的评论做了,它正确调整了大小。为了获得画布的大小,我将监听器切换到堆栈窗格,并打印了newValue。那应该是正确的尺寸。

标签: java canvas layout javafx


【解决方案1】:

从您的 initialize() 方法中删除绑定。您正在尝试自己控制布局操作,这会阻止 StackPane 做它应该做的事情。这将适当地调整Canvas 的大小。

您声明当您打印Canvaswidthheight 时,它返回-1。而不是这样做,打印容器StackPanewidthheight。毕竟Canvas 完全包含在StackPane 中,所以widthheight 将几乎相同,即使不相同。

【讨论】:

  • 你好 Michael 我修改了代码,尝试的一切都不起作用....我已将 Controller.java 更改为没有绑定,也尝试不修改 Canvas 的宽度但随后它没有被绘制。虽然当我修改 Canvas 的宽度和高度时,它会被绘制,如您在第二张图片中看到的那样 :) 感谢您的帮助...
  • 在你的 width 和 height 属性监听器中取出 resizeVisualizer 调用。
  • 我需要一种方法将 canvasWidthcanvasHeight 绑定到 Canvas 的 widthheight 而不使用绑定。这是出于性能原因,因为我正在绘制动画Canvas
  • 使用 StackPanes 的宽度和高度。画布会填满它,因此它的高度和宽度就是画布的
  • 删除所有内容并尝试使用 visualizerStackPane.getWidth() 或使用 layoutBounds() 。你能提供一些有效的代码吗? :) 出于某种原因,Canvas 的大小为 (0,0) ,它不会调整大小......
【解决方案2】:

经过研究,我找到了解决方案。从Canvas 创建自定义Canvas 类和@Overriding 方法:


?很高兴看到这个问题的最终答案:How to make canvas Resizable in javaFX?

?给我灵感的链接:http://dlsc.com/2014/04/10/javafx-tip-1-resizable-canvas/


ResizableCavas.java:

import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

class ResizableCanvas extends Canvas {

    GraphicsContext gc                  = getGraphicsContext2D();
    int             canvasWidth         = 0;
    int             canvasHeight        = 0;
    int             halfCanvasHeight    = 0;

    /**
     * Constructor
     */
    public ResizableCanvas() {

        // if i didn't add the draw to the @Override resize(double width, double
        // height) then it must be into the below listeners

        // Redraw canvas when size changes.
        widthProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println("Entered WIDTH property");
            canvasWidth = (int) widthProperty().get();
        });
        heightProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println("Entered HEIGHT property");

            canvasHeight = (int) heightProperty().get();
            halfCanvasHeight = canvasHeight >> 1;
        });

    }

    /**
     * Redraw the Canvas
     */
    private void draw() {

        System.out.println(" Real Canvas Width is:" + getWidth() + " , Real Canvas Height is:" + getHeight() + "\n");

        gc.clearRect(0, 0, canvasWidth, canvasHeight);

        gc.setStroke(Color.RED);
        gc.strokeLine(0, 0, canvasWidth, canvasHeight);
        gc.strokeLine(0, canvasHeight, canvasWidth, 0);
    }

    @Override
    public double minHeight(double width) {
        return 1;
    }

    @Override
    public double maxHeight(double width) {
        return Double.MAX_VALUE;
    }

    @Override
    public double prefHeight(double width) {
        return minHeight(width);
    }

    @Override
    public double minWidth(double height) {
        return 1;
    }

    @Override
    public double maxWidth(double height) {
        return Double.MAX_VALUE;
    }

    @Override
    public boolean isResizable() {
        return true;
    }

    @Override
    public void resize(double width, double height) {
        super.setWidth(width);
        super.setHeight(height);
        draw();
    }
}

Controller.java:

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;

public class Controller extends StackPane {

    @FXML
    private GridPane container;

    @FXML
    private GridPane topGridPane;

    @FXML
    private StackPane visualizerStackPane;

    @FXML
    private VBox topRightVBox;

    @FXML
    private StackPane mediaFileStackPane;

    @FXML
    private GridPane bottomGridPane;

    // ------------------------------

    ResizableCanvas visualizerCanvas = new ResizableCanvas();

    /**
     * Constructor
     */
    public Controller() {

        // ------------------------FXMLLoader ---------------------------------
        FXMLLoader loader = new FXMLLoader(getClass().getResource("Controller.fxml"));
        loader.setController(this);
        loader.setRoot(this);

        try {
            loader.load();
        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Controller FXML can't be loaded!", ex);
        }
    }

    /**
     * Called as soon as FXML FILE has been loaded
     */
    @FXML
    private void initialize() {

        visualizerStackPane.getChildren().add(visualizerCanvas);

    }
}

【讨论】:

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