How to make canvas Resizable in javaFX?

有些话、适合烂在心里 提交于 2019-11-29 02:00:03

There's a guide that I think that you may find useful for setting up a resizable canvas:

JavaFx tip - resizable canvas

Piece of code from the guide:

/**
 * 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();
        }
    }

Taken from http://werner.yellowcouch.org/log/resizable-javafx-canvas/: To make a JavaFx canvas resizable all that needs to be done is override the min/pref/max methods. Make it resizable and implement the resize method.

With this method no width/height listeners are necessary to trigger a redraw. It is also no longer necessary to bind the size of the width and height to the container.

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>
}

The canvas class just needs to override isResizable() (everything else, which is suggested in other examples, is actually not necessary) :

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

And in the Application the width and height properties of the canvas have to be bound to the canvas' parent:

@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);
    ...
}

Listeners can be added to the width in height properties, in order to redraw the canvas, when it is resized (but if you need that and where to place it, depends on your application):

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

I found that the above solutions did not work when the canvas is contained in a HBox, as the HBox would not shrink when window is resized because it would clip the canvas. Thus the HBox would expand, but never grow any smaller. I used the following code to make the canvas fit the container:

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

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

And in my controller class:

@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);
    ...
}

Of all the methods shown, this was the only one that actually made the canvas truly resizable:

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)
    }
}

For me, I didn't want to break encapsulation by forcing the parent component to send us a width and height; I also did not want to make the canvas the only child in it's parent by taking up all the space; and finally the canvas was being drawn to in another class, so the drawing method did not live inside the canvas.

With this canvas, I do not need to bind to its parent width/height properties to make the canvas resize. It just resizes with whatever size the parent chooses. In addition, anyone using the canvas can just bind to its width/height properties and manage their own drawing when these properties change.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!