GOXR3PLUS
GOXR3PLUS

Reputation: 7255

JavaFX Canvas not resizing correctly inside StackPane

Below is the Main.class and the Controller.class , i tried to make it as simpler as possible.

The program outputs the below window: enter image description here


The black is a StackPane named visualizerStackPane and inside it is a Canvas named visualizerCanvas . What i want to do is the Canvas inside the StackPane to be resized accordingly to the StackPane size substract 4 . But everything fails when you resize the window.

What you have to try...

1)Maximize the window and then normalize it back.

2)Increase the size of the window and slowly decrease it back.

Why this is happening?I have this problem 3 months now and i can't find a solution...

I have also looked JavaFX - Resize Canvas when screen is resized but the problem here seems to be different...


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:

I have modified the Controller class to paint the Canvas and removed the bindings from Initialize method , it doesn't work it produces the below:

enter image description here

If i don't set the width and height of the Canvas manually it doesn't even get painted...I don't know why .

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>

Finally:

A big thanks to the person who will manage to solve this problem :).

Upvotes: 2

Views: 2767

Answers (2)

GOXR3PLUS
GOXR3PLUS

Reputation: 7255

After research i have found the solution . Creating a custom Canvas class and @Overriding methods from Canvas:


🌾Very glad from the final answer on this question: How to make canvas Resizable in javaFX?

🌾A link that gave me the idea: 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);

    }
}

Upvotes: 1

Hypnic Jerk
Hypnic Jerk

Reputation: 1192

Remove the binding from your initialize() method. You are attempting to take control of layout operations yourself, which stop StackPane doing what it is supposed to do. This will resize the Canvas appropriately.

You state that when you print the width and height of the Canvas, it returns -1. Instead of doing this, print the width and height of the container StackPane. Since Canvas is, after all, contained fully within the StackPane, then the width and height will be almost identical, if not identical.

Upvotes: 1

Related Questions