我最近使用JavaFX使用鼠标输入来生成绘画程序。我遇到了一个尝试在画布上放置一个矩形的问题-它似乎没有到达我想要的位置。我花了数小时试图破解这个问题,我认为我需要一些帮助。代码如下:
问题似乎是当我移动不想发生的鼠标时,坐标会不断更新。我想要的是让用户单击并拖动鼠标,然后最后根据鼠标移动的坐标生成矩形。线条工具也会发生相同的情况,因为它将像笔刷工具一样工作,并且随着鼠标在屏幕上移动,将放下越来越多的形状。所有这些都在控制器类中。
Main Class
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(FXMLLoader.load(getClass().getResource("sample.fxml"))));
stage.setTitle("Paint");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller
package sample;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.io.File;
public class Controller {
int prevX, prevY, startX, startY, x , y;
boolean dragging;
@FXML
private Canvas canvas;
@FXML
private ColorPicker colorPicker;
@FXML
private TextField brushSize;
@FXML
private ToggleButton eraser;
@FXML
private ToggleButton line;
@FXML
private ToggleButton rectangle;
public void initialize() {
GraphicsContext g = canvas.getGraphicsContext2D();
canvas.setOnMousePressed(e -> {
startX = prevX = x = (int) e.getX();
startX = prevY = y = (int) e.getY();
dragging = true;
});
canvas.setOnMouseReleased(e -> {
dragging = false;
});
canvas.setOnMouseDragged(e -> {
double size = Double.parseDouble(brushSize.getText());
if(!dragging)
return;
double x = e.getX();
double y = e.getY();
if(eraser.isSelected()) {
g.clearRect(x, y, size, size);
} else if(line.isSelected()) {
g.strokeLine(prevX, prevY, x, y); // also experiencing issues
} else if(rectangle.isSelected()) {
putRect(g, false, startX, startY, x, y); // this puts the rectangle on the canvas
} else {
g.setStroke(colorPicker.getValue());
g.setLineWidth(size);
g.strokeLine(prevX, prevY, x, y);
}
prevX = (int) x;
prevY = (int) y;
});
}
public void putRect(GraphicsContext g, boolean filled, int x1, int y1, int x2, int y2) { // to create the rectangle
if(x1 == x2 || y1 == y2) {
return;
} else {
int x = Math.min(x1,x2); // get upper left corner, (x,y)
int y = Math.min(y1,y2);
int w = Math.abs(x1 - x2); // get width and height
int h = Math.abs(y1 - y2);
if (filled) {
g.fillRect(x, y, w, h);
} else {
g.strokeRect(x, y, w, h);
}
}
}
public void onSave() {
try{
Image snapshot = canvas.snapshot(null, null);
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File("paintsave.png"));
} catch (Exception e) {
System.out.println("Failed to save: " + e);
}
}
public void onExit() {
Platform.exit();
}
}
FXML
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ColorPicker?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.control.ToggleButton?>
<BorderPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml"
prefHeight="600.0" prefWidth="600.0">
<top>
<VBox>
<MenuBar>
<Menu text="file">
<MenuItem text="Save" onAction="#onSave"/>
<MenuItem text="Exit" onAction="#onExit"/>
</Menu>
</MenuBar>
<ToolBar>
<HBox alignment="CENTER" spacing="5">
<TextField fx:id="brushSize" text="18"/>
<ColorPicker fx:id="colorPicker"/>
<ToggleButton fx:id="eraser" text="Eraser"/>
<ToggleButton fx:id="line" text="Line"/>
<ToggleButton fx:id="rectangle" text="Rectangle"/>
</HBox>
</ToolBar>
</VBox>
</top>
<center>
<Canvas fx:id="canvas" width="600" height="600"/>
</center>
</BorderPane>
下面的代码部分是有问题的:
canvas.setOnMouseDragged(e -> {
double size = Double.parseDouble(brushSize.getText());
if(!dragging)
return;
double x = e.getX();
double y = e.getY();
if(eraser.isSelected()) {
g.clearRect(x, y, size, size);
} else if(line.isSelected()) {
g.strokeLine(prevX, prevY, x, y); // also experiencing issues
} else if(rectangle.isSelected()) {
putRect(g, false, startX, startY, x, y); // this puts the rectangle on the canvas
} else {
g.setStroke(colorPicker.getValue());
g.setLineWidth(size);
g.strokeLine(prevX, prevY, x, y);
}
prevX = (int) x;
prevY = (int) y;
});
您看到,每次移动鼠标都会调用此函数。这使您可以看到生产线的动态变化,但会带来一些其他问题。每次移动鼠标时,都会使用新坐标绘制新对象,但是不会删除以前的对象,尽管应该删除。
我建议使用两种不同的方法来解决此问题:
希望这样对您有用。