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

问题描述 投票:0回答:1

我最近使用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>
java user-interface javafx model-view-controller paint
1个回答
0
投票

下面的代码部分是有问题的:

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. 保存上次调用此功能时鼠标所在的坐标。因此,每次调用when函数时,都应从上一次重画对象,但要用背景色填充它。这应该像您上次绘制的“删除”对象一样。如果您的对象不重叠,这应该可以工作。如果这样做,则可能会删除其中的一部分。
  2. 开始绘图之前创建画布的副本。因此,每次调用函数时,请将画布重置为副本的状态,然后绘制对象。

希望这样对您有用。

© www.soinside.com 2019 - 2024. All rights reserved.