JavaFX 绘制形状时出现鼠标输入问题?

标签 java user-interface javafx model-view-controller paint

我最近使用 JavaFX 来生成一个使用鼠标输入的绘画程序。我在尝试将矩形放到 Canvas 上时遇到了一个问题 - 它似乎没有到达我想要的位置。我花了几个小时试图解决这个问题,我想我需要一些帮助。代码如下:

问题似乎是当我移动鼠标时坐标不断更新,这是我不希望发生的。我想要的是用户单击并拖动鼠标,最后,将根据鼠标移动的坐标生成矩形。线条工具也会发生同样的情况,因为它的工作方式与画笔工具一样,当鼠标在屏幕上移动时,会放下越来越多的形状。所有这些都在 Controller 类中。

主类

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;
});

你看,每次移动鼠标时都会调用此函数。这允许您看到线路的动态变化,但会带来一些额外的问题。每次移动鼠标时,都会使用新坐标绘制新对象,但以前的对象不会被删除,尽管它应该被删除。

我建议两种不同的方法来解决这个问题:

  1. 保存上次调用该函数时鼠标所在位置的坐标。因此,每次调用函数时,您都应该重新绘制上次的对象,但用背景颜色填充它。这应该像“删除”您上次绘制的对象一样。如果您的对象不重叠,这应该有效。如果他们这样做,这可能会删除其中的一部分。
  2. 在开始绘图之前创建 Canvas 的副本。因此,每次调用函数时,将 Canvas 重置为副本的状态,然后绘制对象。

希望本文中的某些内容对您有用。

关于JavaFX 绘制形状时出现鼠标输入问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61040994/

相关文章:

java - 从枚举返回字符串文字

java - Android 操作栏标题文本在某些手机上不可点击

JavaFX,命令顺序(看似)被忽略

java - 在 JavaFX 中显示变量的值

java - 如何在 ICEpdf 上删除 pdf 页面周围的薄背框?

java - -ear//.war//.POST_MODULE错误(java.lang.NoClassDefFoundError)

java - 在 Java EE 中绘制 google 图表

Java-RAM 和静态变量的好处

c# - 从 UI 线程中的异步组件触发事件

c++ - 在 MFC 中,为什么我需要创建 CWinApp 的全局实例?