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

JavaFX绘图应用:创建独立Canvas事件处理类优化控制器

JavaFX绘图应用:Canvas事件处理逻辑独立化方案

方案一:独立EventHandler实现类

无需修改Canvas本身,将每种鼠标事件的处理逻辑封装到独立的EventHandler实现类中,通过构造函数注入依赖(如当前工具、画笔大小、颜色选择器等),彻底剥离控制器中的事件代码。

1. 鼠标按下事件处理器

public class CanvasMousePressedHandler implements EventHandler<MouseEvent> {
    private final String tool;
    private final double currentSize;
    private final ColorPicker colorPicker;
    private final GraphicsContext drawing;
    private double x1;
    private double y1;

    // 构造函数注入所有依赖
    public CanvasMousePressedHandler(String tool, double currentSize, ColorPicker colorPicker, GraphicsContext drawing) {
        this.tool = tool;
        this.currentSize = currentSize;
        this.colorPicker = colorPicker;
        this.drawing = drawing;
    }

    @Override
    public void handle(MouseEvent e) {
        x1 = e.getX();
        y1 = e.getY();
        switch (tool) {
            case "Brush":
                drawing.setLineWidth(currentSize);
                drawing.setStroke(colorPicker.getValue());
                drawing.beginPath();
                drawing.setLineCap(StrokeLineCap.ROUND);
                drawing.moveTo(x1, y1);
                break;
            case "Eraser":
                drawing.clearRect(e.getX(), e.getY(), currentSize, currentSize);
                break;
        }
    }

    // 提供getter供其他处理器访问坐标
    public double getX1() { return x1; }
    public double getY1() { return y1; }
}

2. 鼠标拖拽事件处理器

public class CanvasMouseDraggedHandler implements EventHandler<MouseEvent> {
    private final String tool;
    private final double currentSize;
    private final GraphicsContext drawing;
    private boolean dragged;

    public CanvasMouseDraggedHandler(String tool, double currentSize, GraphicsContext drawing) {
        this.tool = tool;
        this.currentSize = currentSize;
        this.drawing = drawing;
    }

    @Override
    public void handle(MouseEvent e) {
        switch (tool) {
            case "Brush":
                dragged = true;
                drawing.lineTo(e.getX(), e.getY());
                drawing.stroke();
                break;
            case "Eraser":
                drawing.clearRect(e.getX(), e.getY(), currentSize, currentSize);
                break;
        }
    }

    public boolean isDragged() { return dragged; }
    public void setDragged(boolean dragged) { this.dragged = dragged; }
}

3. 鼠标释放事件处理器

public class CanvasMouseReleasedHandler implements EventHandler<MouseEvent> {
    private final String tool;
    private final double currentSize;
    private final ColorPicker colorPicker;
    private final GraphicsContext drawing;
    private final CanvasMousePressedHandler pressedHandler;
    private final CanvasMouseDraggedHandler draggedHandler;

    public CanvasMouseReleasedHandler(String tool, double currentSize, ColorPicker colorPicker, GraphicsContext drawing,
                                      CanvasMousePressedHandler pressedHandler, CanvasMouseDraggedHandler draggedHandler) {
        this.tool = tool;
        this.currentSize = currentSize;
        this.colorPicker = colorPicker;
        this.drawing = drawing;
        this.pressedHandler = pressedHandler;
        this.draggedHandler = draggedHandler;
    }

    @Override
    public void handle(MouseEvent e) {
        switch (tool) {
            case "Brush":
                drawing.closePath();
                if (!draggedHandler.isDragged()) {
                    drawing.setFill(colorPicker.getValue());
                    drawing.fillOval(pressedHandler.getX1(), pressedHandler.getY1(), currentSize + 2, currentSize + 2);
                }
                draggedHandler.setDragged(false);
                break;
            case "Eraser":
                drawing.clearRect(e.getX(), e.getY(), currentSize, currentSize);
                break;
            case "Rectangle":
                UI.createShape(e, pressedHandler.getX1(), pressedHandler.getY1(), colorPicker, tool);
                break;
            case "Oval":
                UI.createShape(e, pressedHandler.getX1(), pressedHandler.getY1(), colorPicker, tool);
                break;
        }
    }
}

4. 控制器中关联处理器

// 控制器中的变量
private String tool;
private double currentSize;
@FXML private ColorPicker colorPicker;
@FXML private Canvas canvas;
private GraphicsContext drawing;

@FXML
public void initialize() {
    drawing = canvas.getGraphicsContext2D();
    
    // 创建处理器实例并注入依赖
    CanvasMousePressedHandler pressedHandler = new CanvasMousePressedHandler(tool, currentSize, colorPicker, drawing);
    CanvasMouseDraggedHandler draggedHandler = new CanvasMouseDraggedHandler(tool, currentSize, drawing);
    CanvasMouseReleasedHandler releasedHandler = new CanvasMouseReleasedHandler(tool, currentSize, colorPicker, drawing, pressedHandler, draggedHandler);
    
    // 绑定到Canvas
    canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, pressedHandler);
    canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, draggedHandler);
    canvas.addEventHandler(MouseEvent.MOUSE_RELEASED, releasedHandler);
}

方案二:自定义Canvas类

将Canvas与事件处理逻辑完全绑定,创建自定义Canvas子类,内部注册事件处理器,通过setter方法接收外部状态。

1. 自定义Canvas实现

public class DrawingCanvas extends Canvas {
    private String tool;
    private double currentSize;
    private ColorPicker colorPicker;
    private GraphicsContext drawing;
    private double x1;
    private double y1;
    private boolean dragged;

    public DrawingCanvas() {
        drawing = this.getGraphicsContext2D();
        registerEventHandlers();
    }

    private void registerEventHandlers() {
        // 鼠标按下事件
        this.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {
            x1 = e.getX();
            y1 = e.getY();
            switch (tool) {
                case "Brush":
                    drawing.setLineWidth(currentSize);
                    drawing.setStroke(colorPicker.getValue());
                    drawing.beginPath();
                    drawing.setLineCap(StrokeLineCap.ROUND);
                    drawing.moveTo(x1, y1);
                    break;
                case "Eraser":
                    drawing.clearRect(e.getX(), e.getY(), currentSize, currentSize);
                    break;
            }
        });

        // 鼠标拖拽事件
        this.addEventHandler(MouseEvent.MOUSE_DRAGGED, e -> {
            switch (tool) {
                case "Brush":
                    dragged = true;
                    drawing.lineTo(e.getX(), e.getY());
                    drawing.stroke();
                    break;
                case "Eraser":
                    drawing.clearRect(e.getX(), e.getY(), currentSize, currentSize);
                    break;
            }
        });

        // 鼠标释放事件
        this.addEventHandler(MouseEvent.MOUSE_RELEASED, e -> {
            switch (tool) {
                case "Brush":
                    drawing.closePath();
                    if (!dragged) {
                        drawing.setFill(colorPicker.getValue());
                        drawing.fillOval(x1, y1, currentSize + 2, currentSize + 2);
                    }
                    dragged = false;
                    break;
                case "Eraser":
                    drawing.clearRect(e.getX(), e.getY(), currentSize, currentSize);
                    break;
                case "Rectangle":
                    UI.createShape(e, x1, y1, colorPicker, tool);
                    break;
                case "Oval":
                    UI.createShape(e, x1, y1, colorPicker, tool);
                    break;
            }
        });
    }

    // 外部设置状态的setter
    public void setTool(String tool) { this.tool = tool; }
    public void setCurrentSize(double currentSize) { this.currentSize = currentSize; }
    public void setColorPicker(ColorPicker colorPicker) { this.colorPicker = colorPicker; }
}

2. FXML与控制器使用

  1. FXML中替换为自定义Canvas(需指定全限定类名):
<com.yourpackage.DrawingCanvas fx:id="canvas" width="800" height="600"/>
  1. 控制器中注入并配置:
@FXML private DrawingCanvas canvas;
@FXML private ColorPicker colorPicker;
private String tool;
private double currentSize;

@FXML
public void initialize() {
    canvas.setTool(tool);
    canvas.setCurrentSize(currentSize);
    canvas.setColorPicker(colorPicker);
}

关键注意事项

  • 若状态(如tool、currentSize)动态变化,建议使用JavaFX的Observable类型(如StringProperty、DoubleProperty),让处理器自动感知状态更新。
  • 自定义Canvas方案需确保FXML引用的类名完整正确,否则无法加载组件。
  • 处理器间共享状态时,可通过构造函数传递实例或使用统一的状态管理对象。

内容的提问来源于stack exchange,提问作者Marek Pospíšil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:33:28