JavaFX中如何在FXML中调整画布大小

3
我正在尝试在Javafx中调整画布的大小。我正在使用场景构建器和FXML。到目前为止,当用户单击画布时,画布会变黑,然后当我调整屏幕大小并单击画布时,只有画布的原始大小变黑(画布未被调整大小)。我不确定如何解决这个问题。任何想法或解决方案都将有很大帮助。
代码:
控制器:
public class MainFXMLController implements Initializable
{

    @FXML
    private Canvas mainCanvas;

    @FXML

    public GraphicsContext gc;

    public void initGraphics()
    {
        gc = mainCanvas.getGraphicsContext2D();
    }

    public void drawClicked(MouseEvent me)
    {

        gc.clearRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
        gc.setFill(Color.BLACK);
        gc.fillRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
        System.out.println("Current mosue position: " + me.getX() + ":" + me.getY());
    }

    @Override
    public void initialize(URL url, ResourceBundle rb)
    {
        initGraphics();

    }  
}

Fxml:

<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="750.0"  xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"  fx:controller="app.MainFXMLController">
 <children>
    <Canvas fx:id="mainCanvas" height="565.0" onMouseClicked="#drawClicked" width="750.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0" />

主要的Java文件:

public class DrawFx extends Application
{

    @Override
    public void start(Stage stage) throws Exception
    {
        Parent root = FXMLLoader.load(getClass().getResource("MainFXML.fxml"));

        Scene scene = new Scene(root);
        stage.setTitle("DrawFx");
        stage.getIcons().add(new Image("/icon/icon.png"));
        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}
2个回答

1

首先是一些Javadocs文档 :)

Canvas节点的构造需要指定画布绘制命令渲染成的图像的宽度和高度。所有的绘画操作都会被裁剪到该图像的边界之内。

所以每次用户调整窗口大小时,我们需要改变画布的宽度,然后重新绘制画布。

让我们从向根布局添加一个fx:id开始。

<AnchorPane fx:id="anchorPane" prefHeight="600.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainFXMLController">
    <children>
        <Canvas fx:id="mainCanvas" height="565.0" onMouseClicked="#drawClicked" width="750.0"   AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="35.0"/>
    </children>
</AnchorPane>

下一步是向根布局添加一个更改监听器,它将把新的高度和宽度设置为画布,然后重新绘制它。我们可以在控制器的initialize()中完成此操作。
public class Controller implements Initializable {

    @FXML
    AnchorPane anchorPane;

    @FXML
    private Canvas mainCanvas;

    @FXML
    public GraphicsContext gc;

    public void initGraphics() {
        gc = mainCanvas.getGraphicsContext2D();
    }

    public void drawClicked() {
        gc.clearRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
        gc.setFill(Color.BLACK);
        gc.fillRect(0, 0, mainCanvas.getWidth(), mainCanvas.getHeight());
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        initGraphics();

        anchorPane.prefWidthProperty().addListener((ov, oldValue, newValue) -> {
            mainCanvas.setWidth(newValue.doubleValue());
            drawClicked();
        });

        anchorPane.prefHeightProperty().addListener((ov, oldValue, newValue) -> {
            mainCanvas.setHeight(newValue.doubleValue());
            drawClicked();
        });
    }
}

我没有为 reDraw()创建新的方法,因为你的 drawClicked()没有做任何事情。但是,一旦更有意义,您可以分开两种方法。
最后一件事是将根布局的 prefWidthProperty() prefHeightProperty()绑定到场景的宽度和高度上。
public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws IOException {
        AnchorPane root = FXMLLoader.load(getClass().getResource("MainFXML.fxml"));

        Scene scene = new Scene(root);

        stage.setTitle("DrawFx");
        stage.setScene(scene);
        stage.show();

        root.prefWidthProperty().bind(scene.widthProperty());
        root.prefHeightProperty().bind(scene.heightProperty());
    }
}

非常感谢,它起作用了!但是我想知道在调用redraw()函数时是否可能保留画布上的数据?因此,如果它可以保留先前的绘图,则不会将画布填充为黑色。 - parth2701
您可以执行与原始画布相同的逻辑,但所涉及的点会按比例缩放到新尺寸。 - ItachiUchiha

1
如果你想在FXML中调整画布大小并且预计之后重绘其内容,那么最基本的设置应该类似于这样:

test.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>

TestController.java

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


包装(这不是功能的一部分,只是为了完整性而提供)

Test.java

package test;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Test extends Application {
  @Override
  public void start(Stage primaryStage) throws Exception {
    FXMLLoader loader=new FXMLLoader(getClass().getResource("test.fxml"));
    Parent root=loader.load();
    primaryStage.setTitle("Test");
    primaryStage.setScene(new Scene(root));
    primaryStage.show();
  }

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

test包的存在是为了允许模块化的魔法。

module-info.java

module cnvtest {
  requires transitive javafx.graphics;
  requires javafx.fxml;
  opens test to javafx.fxml;
  exports test;
}

而且真的没有更多的文件了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接