Как сделать холст Изменяемый размер в javaFX?

В javaFX для изменения размера холста нет такого метода для этого, единственное решение - это расширение из Canvas.

class ResizableCanvas extends Canvas {

    public ResizableCanvas() {
        // Redraw canvas when size changes.
        widthProperty().addListener(evt -> draw());
        heightProperty().addListener(evt -> draw());
    }

    private void draw() {
        double width = getWidth();
        double height = getHeight();

        GraphicsContext gc = getGraphicsContext2D();
        gc.clearRect(0, 0, width, height);

    }

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

простирается от Canvas - единственное решение сделать canvas Resizable? потому что это решение работает только в том случае, если мы не хотим использовать FXML, если мы объявим в fxml холст, как мы можем сделать его изменчивым?

это мой код:

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class Main extends Application {

    Controller controller;

    @Override
    public void start(Stage primaryStage) throws Exception{
        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
        AnchorPane root = loader.load(); // controller initialized
        controller = loader.getController();
        GraphicsContext gc = controller.canvas.getGraphicsContext2D();
        gc.setFill(Color.AQUA);
        gc.fillRect(0, 0, root.getPrefWidth(), root.getPrefHeight());
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(controller.Pane, controller.Pane.getPrefWidth(), controller.Pane.getPrefHeight()));
        primaryStage.show();
    }

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

Ответ 1

Вот руководство, которое, как мне кажется, может оказаться полезным для настройки изменяемого размера холста:

Совет JavaFx - изменяемый размер холста

Кусок кода из руководства:

/**
 * Tip 1: A canvas resizing itself to the size of
 *        the parent pane.
 */
public class Tip1ResizableCanvas extends Application {

    class ResizableCanvas extends Canvas {

        public ResizableCanvas() {
            // Redraw canvas when size changes.
            widthProperty().addListener(evt -> draw());
            heightProperty().addListener(evt -> draw());
        }

        private void draw() {
            double width = getWidth();
            double height = getHeight();

            GraphicsContext gc = getGraphicsContext2D();
            gc.clearRect(0, 0, width, height);

            gc.setStroke(Color.RED);
            gc.strokeLine(0, 0, width, height);
            gc.strokeLine(0, height, width, 0);
        }

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

        @Override
        public double prefWidth(double height) {
            return getWidth();
        }

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

Ответ 2

Взято из http://werner.yellowcouch.org/log/resizable-javafx-canvas/: Чтобы изменить размер холста JavaFx, все, что нужно сделать, переопределяет методы min/pref/max. Сделайте его resizable и реализуйте метод изменения размера.

С помощью этого метода прослушиватели ширины и высоты не нужны, чтобы вызвать перерисовку. Также нет необходимости связывать размер ширины и высоты с контейнером.

public class ResizableCanvas extends Canvas {

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

@Override
public double maxHeight(double width)
{
    return 1000;
}

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

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

@Override
public double maxWidth(double height)
{
    return 10000;
}

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

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

Ответ 3

Я обнаружил, что вышеприведенные решения не работают, когда холст содержится в HBox, так как HBox не будет уменьшаться при изменении размера окна, поскольку будет обрезать холст. Таким образом, HBox будет расширяться, но никогда не станет меньше. Я использовал следующий код, чтобы холст соответствовал контейнеру:

public class ResizableCanvas extends Canvas {
    @Override
    public double prefWidth(double height) {
        return 0;
    }

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

И в моем классе контроллера:

@FXML
private HBox canvasContainer;
private Canvas canvas = new ResizableCanvas();
...
@Override
public void start(Stage primaryStage) throws Exception {
    ...
    canvas.widthProperty().bind(canvasContainer.widthProperty());
    canvas.heightProperty().bind(canvasContainer.
    pane.getChildren().add(canvas);
    ...
}

Ответ 4

Из всех показанных методов это был единственный метод, который действительно позволял изменять размеры холста:

public class ResizableCanvas extends Canvas {

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

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

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

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

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

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

Я не хотел нарушать инкапсуляцию, заставляя родительский компонент отправить нам width и height; Я также не хотел делать холст единственным дочерним объектом в нем, занимая все пространство; и, наконец, в другом классе рисовался холст, поэтому метод рисования не жил внутри холста.

С этим холстом мне не нужно привязывать его родительские свойства width/height, чтобы изменить размер холста. Он просто изменяет размеры с любым размером, выбранным родителем. Кроме того, любой, кто использует холст, может просто привязать его свойства width/height и управлять собственным рисунком при изменении этих свойств.

Ответ 5

Класс canvas просто должен переопределить isResizable() (все остальное, что предлагается в других примерах, на самом деле необязательно):

public class ResizableCanvas extends Canvas
{
  public boolean isResizable()
  {
      return true;
  }
}

И в приложении свойства ширины и высоты холста должны быть привязаны к родительскому элементу canvas:

@Override
public void start(Stage primaryStage) throws Exception
{
    ...
    StackPane pane = new StackPane();
    ResizableCanvas canvas = new ResizableCanvas(width, height);

    canvas.widthProperty().bind(pane.widthProperty());
    canvas.heightProperty().bind(pane.heightProperty());

    pane.getChildren().add(_canvas);
    ...
}

Слушатели могут добавляться к свойствам ширины по высоте, чтобы перерисовать холст, когда он изменен (но если вам это нужно и где его разместить, зависит от вашего приложения):

widthProperty().addListener(this::paint);
heightProperty().addListener(this::paint);