JavaFX实现一个简易的画图程序
为了应付学校的JavaFX作业瞎写了一个非常简单的画图程序。有很多提高和优化的空间,有需要的可以参考下优化,才疏学浅,希望读者不吝赐教。
基本的属性资源接口
import javafx.scene.paint.Color;
/**
* @author Mr.X
*/
public interface PropertyInterface {
double stageWidth = 900;
double stageHeight = 600;
double[] strokeWidths = new double[]{1, 5, 15};
Color[] strokeColors = new Color[]{Color.BLACK, Color.RED, Color.YELLOW, Color.BLUE};
int[] commands = new int[]{0, 1, 2, 3};
}
MenuBar工厂类
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCombination;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
/**
* @ClassName MenuBarFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/4/29 9:26
* @Version 1.0
*/
public class MenuBarFactory implements PropertyInterface{
double strokeWidth = strokeWidths[0];
Color strokeColor = strokeColors[0];
int command = commands[0];
private CanvasFactory canvasFactory = new CanvasFactory();
private Canvas canvas;
private GraphicsContext gc;
private MenuBar menuBar ;
WritableImage snapshot;
public MenuBarFactory(MenuBar menuBar,CanvasFactory canvasFactory) {
this.menuBar = menuBar;
this.canvasFactory = canvasFactory;
canvas = canvasFactory.getCanvas();
gc = canvasFactory.getGc();
}
public MenuBarFactory(CanvasFactory canvasFactory) {
menuBar = new MenuBar();
this.canvasFactory = canvasFactory;
canvas = canvasFactory.getCanvas();
gc = canvasFactory.getGc();
Menu menu0 = new Menu("颜色");
Menu menu1 = new Menu("粗细");
Menu menu2 = new Menu("图形");
Menu menu3 = new Menu("快捷工具");
MenuItem colorR = new MenuItem("红色");
colorR.setOnAction(e -> canvasFactory.setStrokeColor(strokeColors[1]));
MenuItem colorY = new MenuItem("黄色");
colorY.setOnAction(e -> canvasFactory.setStrokeColor(strokeColors[2]));
MenuItem colorB = new MenuItem("蓝色");
colorB.setOnAction(e -> canvasFactory.setStrokeColor(strokeColors[3]));
MenuItem colorD = new MenuItem("黑色");
colorD.setOnAction(e -> canvasFactory.setStrokeColor(strokeColors[0]));
menu0.getItems().addAll(colorD, colorR, colorB, colorY);
MenuItem borderT = new MenuItem("粗");
borderT.setOnAction(e -> canvasFactory.setStrokeWidth(strokeWidths[2]));
MenuItem borderM = new MenuItem("中");
borderM.setOnAction(e -> canvasFactory.setStrokeWidth(strokeWidths[1]));
MenuItem borderS = new MenuItem("细");
borderS.setOnAction(e -> canvasFactory.setStrokeWidth(strokeWidths[0]));
menu1.getItems().addAll(borderS, borderM, borderT);
MenuItem spaceD = new MenuItem("默认");
spaceD.setOnAction(e -> canvasFactory.setCommand(commands[0]));
MenuItem spaceR = new MenuItem("矩形");
spaceR.setOnAction(e -> canvasFactory.setCommand(commands[1]));
MenuItem spaceE = new MenuItem("椭圆");
spaceE.setOnAction(e -> canvasFactory.setCommand(commands[2]));
MenuItem spaceL = new MenuItem("直线");
spaceL.setOnAction(e -> canvasFactory.setCommand(commands[3]));
menu2.getItems().addAll(spaceD, spaceR, spaceE, spaceL);
MenuItem manageC = new MenuItem("清空");
MenuItem manageS = new MenuItem("另存为");
//设置快捷键
manageC.setAccelerator(KeyCombination.valueOf("ctrl+alt+c"));
manageS.setAccelerator(KeyCombination.valueOf("ctrl+s"));
menu3.getItems().addAll(manageC, manageS);
manageC.setOnAction(event -> {
ArrayList<Double> canvasProperty = canvasFactory.getCanvasProperty();
gc.clearRect(0, 0, canvas.getWidth(),canvas.getHeight());
snapshot = null;
});
manageS.setOnAction(event -> {
snapshot = canvas.snapshot(null, null);
BufferedImage bufferedImage = SwingFXUtils.fromFXImage(snapshot, null);
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("保存canvas图片");
fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("PNG", ".png"));
File file = fileChooser.showSaveDialog(canvas.getScene().getWindow());
if (file != null) {
try {
ImageIO.write(bufferedImage, "PNG", file);
} catch (IOException e) {
e.printStackTrace();
}
}
});
menuBar.getMenus().addAll(menu3, menu0, menu1, menu2);
menuBar.setPrefWidth(stageWidth);
}
public MenuBar getMenuBar() {
return menuBar;
}
}
Canvas工厂类
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import java.util.ArrayList;
/**
* @ClassName CanvasFactory
* @Description TODO
* @Author Mr_X
* @Date 2022/4/29 9:34
* @Version 1.0
*/
public class CanvasFactory implements PropertyInterface{
double strokeWidth = strokeWidths[0];
Color strokeColor = strokeColors[0];
int command = commands[0];
double canvasWidth;
double canvasHeight;
ArrayList<Double> properties;
private WritableImage snapshot;
//记录鼠标落下的坐标点
double startX;
double startY;
final Canvas canvas;
final GraphicsContext gc;
public CanvasFactory(Canvas canvas) {
this.canvas = canvas;
canvas.setLayoutX(0.0);
canvas.setLayoutY(0.0);
gc = canvas.getGraphicsContext2D();
}
public CanvasFactory() {
canvas = new Canvas(stageWidth,stageHeight);
gc = canvas.getGraphicsContext2D();
}
public Canvas getCanvas() {
return canvas;
}
public GraphicsContext getGc() {
return gc;
}
public void setStrokeWidth(double strokeWidth) {
this.strokeWidth = strokeWidth;
}
public void setStrokeColor(Color strokeColor) {
this.strokeColor = strokeColor;
}
public void setCommand(int command) {
this.command = command;
}
public void mouseListenerPaint(){
//鼠标落下即可得到起始位置的坐标点
canvas.setOnMousePressed(event -> {
startX = event.getX();
startY = event.getY();
});
//监听鼠标拖拽来绘制图形
canvas.setOnMouseDragged(event -> {
properties = getCanvasProperty();
double beginX = startX;
double beginY = startY;
double endX = event.getX();
double endY = event.getY();
gc.setStroke(strokeColor);
gc.setLineWidth(strokeWidth);
if (command == 1) {
if (endX < startX) {
beginX = endX;
endX = startX;
}
if (endY < startY) {
beginY = endY;
endY = startY;
}
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.drawImage(snapshot, 0, 0, canvas.getWidth(), canvas.getHeight());
gc.strokeRect(beginX, beginY, endX - beginX, endY - beginY);
} else if (command == 0) {
gc.strokeLine(startX, startY, endX, endY);
startX = endX;
startY = endY;
} else if (command == 3) {
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.drawImage(snapshot, 0, 0, canvas.getWidth(), canvas.getHeight());
gc.strokeLine(startX, startY, endX, endY);
} else if (command == 2) {
if (endX < startX) {
beginX = endX;
endX = startX;
}
if (endY < startY) {
beginY = endY;
endY = startY;
}
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
gc.drawImage(snapshot, 0, 0, canvas.getWidth(), canvas.getHeight());
gc.strokeOval(beginX, beginY, endX - beginX, endY - beginY);
}
});
//鼠标落下后将canvas的内容通过快照来记录,但是这也造成一个问题,随着绘次数最多会造成画面失真
canvas.setOnMouseReleased(event -> snapshot = canvas.snapshot(null, null));
}
public ArrayList<Double> getCanvasProperty(){
//监听画布的长高属性并返回。
canvas.widthProperty().addListener((observable, oldValue, newValue) -> canvasWidth = newValue.doubleValue());
canvas.heightProperty().addListener((observable, oldValue, newValue) -> canvasHeight = newValue.doubleValue());
ArrayList<Double> properties = new ArrayList<>();
properties.add(canvasWidth);
properties.add(canvasHeight);
return properties;
}
}
Painter入口类
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.MenuBar;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.InputStream;
/**
* @ClassName PainterTest
* @Description TODO
* @Author Mr_X
* @Date 2022/4/29 9:24
* @Version 1.0
*/
public class PainterTest extends Application implements PropertyInterface{
static {
System.out.println("这是静态代码块\t"+Thread.currentThread().getName());
}
final CanvasFactory canvasFactory = new CanvasFactory();
final MenuBarFactory menuBarFactory = new MenuBarFactory(canvasFactory);
final MenuBar menuBar = menuBarFactory.getMenuBar();
final Canvas canvas = canvasFactory.getCanvas();
public static void main(String[] args) {
System.out.println("这是main方法\t"+Thread.currentThread().getName());
launch(args);
}
@Override
public void init() throws Exception {
System.out.println("这是初始化方法\t"+Thread.currentThread().getName());
}
@Override
public void start(Stage primaryStage) throws Exception {
System.out.println("这是start方法\t"+Thread.currentThread().getName());
canvasFactory.mouseListenerPaint();
AnchorPane anchorPane = new AnchorPane();
anchorPane.setPrefHeight(stageHeight);
anchorPane.setPrefWidth(stageWidth);
anchorPane.getChildren().add(canvas);
BorderPane borderPane = new BorderPane();
borderPane.setTop(menuBar);
borderPane.setCenter(anchorPane);
Scene scene = new Scene(borderPane);
primaryStage.setScene(scene);
primaryStage.setTitle(this.getClass().getSimpleName());
primaryStage.setHeight(stageHeight);
primaryStage.setWidth(stageWidth);
primaryStage.show();
//动态调整画布的长宽
primaryStage.widthProperty().addListener((observable, oldValue, newValue) -> {
anchorPane.setPrefWidth(newValue.doubleValue());
canvas.setWidth(anchorPane.getWidth());
});
primaryStage.heightProperty().addListener((observable, oldValue, newValue) -> {
anchorPane.setPrefHeight(newValue.doubleValue());
canvas.setHeight(anchorPane.getHeight());
});
}
@Override
public void stop() throws Exception {
System.out.println("这是stop方法\t"+Thread.currentThread().getName());
}
}
测试效果
注: 文件结构
传送门==》