如何在javaFX中制作canvas Resizable?

时间:2014-07-02 14:18:53

标签: javafx javafx-2 javafx-8 fxml

在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扩展是唯一使画布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);
    }
}

7 个答案:

答案 0 :(得分:5)

有一个指南,我认为您可能会发现有用的设置可调整大小的画布:

JavaFx tip - resizable canvas

  

指南中的一段代码:

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

答案 1 :(得分:2)

取自http://werner.yellowcouch.org/log/resizable-javafx-canvas/:要使JavaFx画布可调整大小,需要完成的任务是覆盖min / pref / max方法。使其可调整大小并实施调整大小方法

使用此方法不需要宽度/高度侦听器来触发重绘。也不再需要将宽度和高度的大小绑定到容器。

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

答案 2 :(得分:1)

canvas类只需覆盖isResizable()(其他所有内容,在其他示例中建议,实际上不是必需的):

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

在Application中,画布的width和height属性必须绑定到画布'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);
    ...
}

可以将侦听器添加到width属性的宽度中,以便重新绘制画布,调整大小时(但如果需要,可以放置它,取决于您的应用程序):

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

答案 3 :(得分:1)

我发现当画布包含在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 :(得分:0)

在所示的所有方法中,这是唯一使画布真正可调整大小的方法:

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 void resize(double width, double height) {
        this.setWidth(width);
        this.setHeight(height)
    }
}

对我来说,我不想通过强制父组件向我们发送widthheight来破坏封装;我也不想通过占用所有空间使画布成为其父项中的唯一孩子。最终,画布被绘制到另一个类中,因此绘图方法不在画布内部。

使用此画布,无需绑定到其父级width / height属性即可调整画布的大小。它只是根据父母选择的大小调整大小。此外,使用画布的任何人都可以绑定到其width / height属性,并在这些属性更改时管理自己的图形。

答案 5 :(得分:0)

  

仅当我们不想使用FXML时,此解决方案才有效

我不确定可调整大小的画布本身的优点,无论是否带有FXML。通常,您想在上面重绘一些内容,然后就没有画布(画布本身没有内容),但是您将返回到特定于应用程序的代码,就像问题iself和大多数答案都包含一些内容一样。 re/draw()方法。
然后您可以扔掉单独的类,在FXML中进行四个绑定:

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

<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="test.TestController">
  <children>
    <Pane fx:id="pane" VBox.vgrow="ALWAYS">
      <children>
        <Canvas fx:id="canvas" height="${pane.height}" width="${pane.width}"
                onWidthChange="#redraw" onHeightChange="#redraw" />
      </children>
    </Pane>
  </children>
</VBox>

并且仅在Java中实现redraw()

package test;

import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;

public class TestController {
  @FXML
  private Canvas canvas;

  @FXML
  private void redraw() {
    double w=canvas.getWidth();
    double h=canvas.getHeight();
    GraphicsContext gc=canvas.getGraphicsContext2D();
    gc.clearRect(0, 0, w, h);
    gc.beginPath();
    gc.rect(10, 10, w-20, h-20);
    gc.stroke();
  }
}

(如果需要,请在https://stackoverflow.com/a/58915071/7916438中找到合适的主类和模块信息)

答案 6 :(得分:0)

为了实现可调整大小的 Canvas,我将 Canvas 放置在 Pane 中:

<Pane fx:id="canvasPane" >
    <Canvas fx:id="canvas" />
</Pane>

然后我将 Canvas 的高度和宽度属性绑定到控制器的 initialize 方法中的 Pane 属性:

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

这允许 Pane 与 LayoutManager 交互,同时导致 Canvas 填充 Pane。