You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX应用中自定义Canvas图形无法显示的问题求助

问题:JavaFX自定义Canvas无法显示函数图形

我大学教授布置了作业,要求实现一个显示函数图形的简单GUI。教授提供了3个基础文件:FunctionCanvas、FunctionCurve和Main,我自行编写了FXML文件和Controller,但运行后带有函数图形的Canvas始终无法显示,试过网上多种方案都没解决,使用IntelliJ IDEA开发。


教授提供的代码

FunctionCanvas.java

package Aufgabe1.jfx;

import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

final class FunctionCanvas extends Canvas
{
    private double c1 = 1, d1 = 0, c2 = 1, d2 = 0;

    void setFunctionParameters(double c1, double d1, double c2, double d2)
    {
        this.c1 = c1;
        this.d1 = d1;
        this.c2 = c2;
        this.d2 = d2;
    }

    FunctionCanvas() {}

    private static final float v = 244f / 255;
    private static final Color backGroundColor = Color.color(0, 0, 0);
    private static final int yOffset = 20;

    void drawFunctionCurve() {
        GraphicsContext gc = getGraphicsContext2D();
        //float v = 244f / 255;
        gc.setFill(backGroundColor);
        gc.fillRect(0, 0, getWidth(), getHeight());

        double h = this.getHeight() - 2 * yOffset;
        int xOffset = 40;
        double w = this.getWidth() - 2 * xOffset;
        double yZero = yOffset + h / 2;
        gc.setFill(Color.BLACK);
        gc.fillText("f(x)", xOffset - 30, yOffset);
        gc.fillText("0", xOffset - 25, yZero + 5);
        gc.fillText("2π", xOffset + w - 5, yZero - 5);
        gc.setLineWidth(1.0);
        gc.setStroke(Color.GRAY);
        gc.strokeLine(xOffset, yOffset, xOffset, yOffset + h);
        gc.strokeLine(xOffset, yZero, xOffset + w, yZero);
        gc.setLineWidth(1.5);
        gc.setStroke(Color.INDIANRED);
        for (int x = 0; x < w; x++) {
            int y = (int) ((h / 2) * sinCosFunc(x * 2 * Math.PI / w));
            gc.strokeLine(xOffset + x, yZero - y, xOffset + x, yZero - y);
        }
    }

    private double sinCosFunc(double x)
    {
        return Math.sin(c1 * x + d1) * Math.cos(c2 * x + d2);
    }
}

FunctionCurve.java

package Aufgabe1.jfx;

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

public class FunctionCurve extends Application {
    public FunctionCurve() { }
    @Override
    public void start(Stage stage) throws Exception {
        int width = 670;
        int height = 520;
        //Written by me
        FXMLLoader loader = new FXMLLoader(getClass().getResource("1gui.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root, width, height);
        stage.setTitle(this.getClass().getSimpleName());
        stage.setScene(scene);
        stage.centerOnScreen();
        stage.show();

    }
}

Main.java

package Aufgabe1.jfx;

final class Main
{
    private Main() {}

    public static void main(String[] args)
    {
        javafx.application.Application.launch(FunctionCurve.class, args);
    }
}

自行编写的代码

1gui.fxml

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

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<SplitPane dividerPositions="0.08892617449664429" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" orientation="VERTICAL" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Aufgabe1.jfx.Controller">
  <items>
    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
         <children>
            <HBox prefHeight="48.0" prefWidth="597.0">
               <children>
                  <Label prefHeight="25.0" prefWidth="25.0" text="c1">
                     <padding>
                        <Insets left="5.0" right="5.0" />
                     </padding>
                  </Label>
                  <Spinner editable="true" prefHeight="10.0" prefWidth="50.0" />
                  <Label prefHeight="25.0" prefWidth="25.0" text="d1">
                     <padding>
                        <Insets left="5.0" right="5.0" />
                     </padding>
                  </Label>
                  <Spinner editable="true" prefHeight="10.0" prefWidth="50.0" />
                  <Label prefHeight="25.0" prefWidth="25.0" text="c2">
                     <padding>
                        <Insets left="5.0" right="5.0" />
                     </padding>
                  </Label>
                  <Spinner editable="true" prefHeight="10.0" prefWidth="50.0" />
                  <Label prefHeight="25.0" prefWidth="25.0" text="d2">
                     <padding>
                        <Insets left="5.0" right="5.0" />
                     </padding>
                  </Label>
                  <Spinner editable="true" prefHeight="10.0" prefWidth="50.0" />
                  <Button mnemonicParsing="false" text="Button">
                     <HBox.margin>
                        <Insets left="5.0" right="5.0" />
                     </HBox.margin>
                  </Button>
                  <Button mnemonicParsing="false" text="reset">
                     <HBox.margin>
                        <Insets left="5.0" right="5.0" />
                     </HBox.margin>
                  </Button>
               </children>
            </HBox>
         </children></AnchorPane>
      <AnchorPane fx:id="_canvas" prefHeight="600.0" prefWidth="400.0" />
  </items>
</SplitPane>

Controller.java

package Aufgabe1.jfx;

import javafx.fxml.FXML;
import javafx.scene.layout.AnchorPane;

public class Controller {

    @FXML
    private AnchorPane _canvas ;

    // called by the FXML loader after the labels declared above are injected:
    public void initialize() {
        FunctionCanvas c = new FunctionCanvas();
        _canvas.setPrefWidth(400);
        _canvas.setPrefHeight(300);
        _canvas.getChildren().add(c);
        c.drawFunctionCurve();

    }
}

问题原因及修复方案

1. Canvas未设置有效尺寸

FunctionCanvas继承自Canvas,默认宽高为0,即使调用绘制方法也无法显示内容。修改Controller的initialize方法,让Canvas绑定父容器尺寸并随容器变化:

public void initialize() {
    FunctionCanvas c = new FunctionCanvas();
    // 绑定Canvas尺寸到父AnchorPane
    c.widthProperty().bind(_canvas.widthProperty());
    c.heightProperty().bind(_canvas.heightProperty());
    // 设置Canvas填充整个AnchorPane
    AnchorPane.setTopAnchor(c, 0.0);
    AnchorPane.setBottomAnchor(c, 0.0);
    AnchorPane.setLeftAnchor(c, 0.0);
    AnchorPane.setRightAnchor(c, 0.0);
    
    _canvas.getChildren().add(c);
    
    // 监听尺寸变化,自动重绘
    c.widthProperty().addListener((obs, oldVal, newVal) -> c.drawFunctionCurve());
    c.heightProperty().addListener((obs, oldVal, newVal) -> c.drawFunctionCurve());
    
    // 初始绘制
    c.drawFunctionCurve();
}

2. 背景色与文字/坐标轴颜色冲突

背景色设为黑色,而文字和坐标轴线条也是深色,导致内容被遮挡。修改FunctionCanvas中的背景色:

private static final Color backGroundColor = Color.WHITE;

3. 曲线绘制方式无效

原代码用strokeLine绘制单个点(起点和终点相同),几乎看不到。修改为连接连续点绘制线段:

gc.setLineWidth(1.5);
gc.setStroke(Color.INDIANRED);
// 记录前一个点的坐标
double prevY = yZero - ((h / 2) * sinCosFunc(0));
for (int x = 1; x < w; x++) {
    double currentX = xOffset + x;
    double currentY = yZero - ((h / 2) * sinCosFunc(x * 2 * Math.PI / w));
    // 绘制前一个点到当前点的线段
    gc.strokeLine(xOffset + x - 1, prevY, currentX, currentY);
    prevY = currentY;
}

修改完成后,Canvas就能正常显示函数图形了。

内容的提问来源于stack exchange,提问作者Marius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 18:05:55