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

huangapple 未分类评论45阅读模式
英文:

JavaFX problem with mouse input when drawing shapes?

问题

我最近开始使用JavaFX编写绘图程序,利用鼠标输入进行绘图。我遇到了一个问题,尝试将矩形放在画布上,但似乎位置不对。我已经花了很多时间尝试解决这个问题,我认为我需要一些帮助。以下是代码:

主类:

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

控制器:

package sample;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleButton;

public class Controller {
    // ... 其他变量和方法 ...

    @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() {
        // ... 初始化代码 ...

        canvas.setOnMousePressed(e -> {
            // ... 鼠标按下事件代码 ...
        });

        canvas.setOnMouseReleased(e -> {
            // ... 鼠标释放事件代码 ...
        });

        canvas.setOnMouseDragged(e -> {
            // ... 鼠标拖拽事件代码 ...
        });
    }

    // ... 其他方法 ...
}

FXML:

<?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.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>
英文:

I've recently taken to JavaFX to produce a paint program using mouse inputs. I've come across an issue trying to put a rectangle onto my canvas - it doesn't seem to go where I want. I've spent hours trying to crack this and I think I need some help. Code is below:

The problem seems to be that the co-ordinates keep updating as I'm moving the mouse which I don't want to happen. What I want is for the user to click and drag the mouse then at the end, the rectangle will be produced based on the coordinates of mouse movement. The same thing happens with the line tool as well, as it will work just like the brush tool and more and more shapes are put down as the mouse is moved across the screen. All of this is in the controller class.

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(&quot;sample.fxml&quot;))));
        stage.setTitle(&quot;Paint&quot;);
        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 -&gt; {
            startX = prevX = x = (int) e.getX();
            startX = prevY = y = (int) e.getY();
            dragging = true;
        });

        canvas.setOnMouseReleased(e -&gt; {
            dragging = false;
        });

        canvas.setOnMouseDragged(e -&gt; {
            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), &quot;png&quot;, new File(&quot;paintsave.png&quot;));
        } catch (Exception e) {
            System.out.println(&quot;Failed to save: &quot; + e);
        }
    }

    public void onExit() {
        Platform.exit();
    }
}

FXML

&lt;?import javafx.geometry.Insets?&gt;
&lt;?import javafx.scene.layout.GridPane?&gt;

&lt;?import javafx.scene.control.Button?&gt;
&lt;?import javafx.scene.control.Label?&gt;

&lt;?import javafx.scene.layout.BorderPane?&gt;
&lt;?import javafx.scene.layout.VBox?&gt;
&lt;?import javafx.scene.control.MenuBar?&gt;
&lt;?import javafx.scene.control.Menu?&gt;
&lt;?import javafx.scene.control.MenuItem?&gt;
&lt;?import javafx.scene.control.ToolBar?&gt;
&lt;?import javafx.scene.layout.HBox?&gt;
&lt;?import javafx.scene.control.TextField?&gt;
&lt;?import javafx.scene.control.ColorPicker?&gt;
&lt;?import javafx.scene.control.CheckBox?&gt;
&lt;?import javafx.scene.canvas.Canvas?&gt;
&lt;?import javafx.scene.control.ToggleButton?&gt;
&lt;BorderPane fx:controller=&quot;sample.Controller&quot;
            xmlns:fx=&quot;http://javafx.com/fxml&quot;
            prefHeight=&quot;600.0&quot; prefWidth=&quot;600.0&quot;&gt;

    &lt;top&gt;
        &lt;VBox&gt;
            &lt;MenuBar&gt;
                &lt;Menu text=&quot;file&quot;&gt;
                    &lt;MenuItem text=&quot;Save&quot; onAction=&quot;#onSave&quot;/&gt;
                    &lt;MenuItem text=&quot;Exit&quot; onAction=&quot;#onExit&quot;/&gt;
                &lt;/Menu&gt;
            &lt;/MenuBar&gt;

            &lt;ToolBar&gt;
                &lt;HBox alignment=&quot;CENTER&quot; spacing=&quot;5&quot;&gt;
                    &lt;TextField fx:id=&quot;brushSize&quot; text=&quot;18&quot;/&gt;
                    &lt;ColorPicker fx:id=&quot;colorPicker&quot;/&gt;
                    &lt;ToggleButton fx:id=&quot;eraser&quot; text=&quot;Eraser&quot;/&gt;
                    &lt;ToggleButton fx:id=&quot;line&quot; text=&quot;Line&quot;/&gt;
                    &lt;ToggleButton fx:id=&quot;rectangle&quot; text=&quot;Rectangle&quot;/&gt;
                &lt;/HBox&gt;
            &lt;/ToolBar&gt;
        &lt;/VBox&gt;
    &lt;/top&gt;

    &lt;center&gt;
        &lt;Canvas fx:id=&quot;canvas&quot; width=&quot;600&quot; height=&quot;600&quot;/&gt;
    &lt;/center&gt;

&lt;/BorderPane&gt;

答案1

得分: 0

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

canvas.setOnMouseDragged(e -&gt; {
  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. 在开始绘制之前创建画布的副本。因此,每次调用函数时,将画布重置为副本的状态,然后再绘制您的对象。

希望其中的一些方法对您有所帮助。

英文:

Following part of the code is problematic:

canvas.setOnMouseDragged(e -&gt; {
  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;
});

You see, this function is called every time that mouse is moved. That allows you seeing your line changes dinamically but introduces some additional problems. Each time that mouse is moved, new object is drawn with new coordinates, but previous object is not deleted, although it should be.

I suggest two different approaches for solving this problem:

  1. Saving the coordinates of where the mouse was when this function was called last time. So, each when function is called, you should redraw the object from last time but filling it with the background color. That should act like "deleting" object you draw last time. This should work if your objects don't overlap. If they do, this could delete part of them.
  2. Creating the copy of canvas before you start drawing. So, each time when function is called, reset the canvas to the state of the copy and then draw your object.

Hope something of this will work for you.

huangapple
  • 本文由 发表于 2020年4月5日 18:12:50
  • 转载请务必保留本文链接:https://java.coder-hub.com/61040994.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定