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与控制器使用
- FXML中替换为自定义Canvas(需指定全限定类名):
<com.yourpackage.DrawingCanvas fx:id="canvas" width="800" height="600"/>
- 控制器中注入并配置:
@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
相关产品推荐
相关产品推荐

