Implementing Paint in JavaFX

2

I'm trying to implement a Paint-like application with JavaFX. I can draw a line from one point to another, but it only appears after I release the button at the end point.

When I try to implement MouseEvent._MOUSE_DRAGGED, several lines are drawn while I'm dragging the mouse cursor.

I want something more natural like Paint itself!

public class PaintApp extends Application {

// TODO: Instance Variables for View Components and Model
Canvas c;
GraphicsContext gc;
GeometricObject go;
ColorPicker colorPicker;
Button drawLine, drawRectangle;
TextField defineWidth;
private boolean isDrawLine, isDrawRectangle;
double initX = 0, initY = 0, finalX = 0, finalY = 0, width;

private void actionHandler(ActionEvent event) {
    if (event.getSource().equals(drawLine)) {
        isDrawLine = true;
    }
}

private void pressHandler(MouseEvent me) {
    initX = me.getX();
    initY = me.getY();
    gc.moveTo(initX, initY);
}

private void releaseHandler(MouseEvent me) {
    finalX = me.getX();
    finalY = me.getY();
    gc.lineTo(finalX, finalY);
    gc.stroke();
}

private void moveHandler(MouseEvent me) {
            finalX = me.getX();
    finalY = me.getY();
    gc.setFill(colorPicker.getValue());
    gc.setLineWidth(1);
    gc.strokeLine(initX, initY, finalX, finalY);
}

// TODO: Private Event Handlers and Helper Methods
/**
 * This is where you create your components and the model and add event
 * handlers.
 *
 * @param stage The main stage
 * @throws Exception
 */
@Override
public void start(Stage stage) throws Exception {
    Pane root = new Pane();
    Scene scene = new Scene(root, 1400, 900); // set the size here
    stage.setTitle("FX GUI Template"); // set the window title here
    stage.setScene(scene);
    // TODO: Add your GUI-building code here

    // 1. Create the model
    // 2. Create the GUI components
    c = new Canvas(1400, 700);
    colorPicker = new ColorPicker(Color.BLACK);
    drawLine = new Button("Line");
    drawRectangle = new Button("Rectangle");
    defineWidth = new TextField("Width");

    // 3. Add components to the root
    root.getChildren().addAll(c, colorPicker, drawLine, defineWidth);

    // 4. Configure the components (colors, fonts, size, location)
    //style canvas
    gc = c.getGraphicsContext2D();
    gc.setFill(Color.WHITE);
    gc.fillRect(0, 0, 1400, 700);

    // style ColorPicker
    colorPicker.relocate(250, 800);

    // style drawLine 
    drawLine.relocate(30, 750);

    // style defineWidth
    defineWidth.relocate(30, 780);

    // 5. Add Event Handlers and do final setup
    c.addEventHandler(MouseEvent.MOUSE_DRAGGED, this::moveHandler);
    c.addEventHandler(MouseEvent.MOUSE_PRESSED, this::pressHandler);
    c.addEventHandler(MouseEvent.MOUSE_RELEASED, this::releaseHandler);
    drawLine.setOnAction(this::actionHandler);
    defineWidth.setOnAction(this::actionHandler);

    // 6. Show the stage
    stage.show();
}

/**
 * Make no changes here.
 *
 * @param args unused
 */
public static void main(String[] args) {
    launch(args);
}

}

    
asked by anonymous 03.08.2018 / 23:36

2 answers

0

Example of an application, however using fxml.

FXML:     

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<?import javafx.scene.canvas.Canvas?>
<BorderPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="com.almasb.paint.PaintController"
            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" />
                    <CheckBox fx:id="eraser" text="Eraser" />
                </HBox>
            </ToolBar>
        </VBox>
    </top>

    <center>
        <Canvas fx:id="canvas" width="600" height="600" />
    </center>

</BorderPane>

CONTROLLER:

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.image.Image;

import javax.imageio.ImageIO;
import java.io.File;

public class PaintController {

    @FXML
    private Canvas canvas;

    @FXML
    private ColorPicker colorPicker;

    @FXML
    private TextField brushSize;

    @FXML
    private CheckBox eraser;

    public void initialize() {
        GraphicsContext g = canvas.getGraphicsContext2D();

        canvas.setOnMouseDragged(e -> {
            double size = Double.parseDouble(brushSize.getText());
            double x = e.getX() - size / 2;
            double y = e.getY() - size / 2;

            if (eraser.isSelected()) {
                g.clearRect(x, y, size, size);
            } else {
                g.setFill(colorPicker.getValue());
                g.fillRect(x, y, size, size);
            }
        });
    }

    public void onSave() {
        try {
            Image snapshot = canvas.snapshot(null, null);

            ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File("paint.png"));
        } catch (Exception e) {
            System.out.println("Failed to save image: " + e);
        }
    }

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

MAIN:

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class PaintApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(FXMLLoader.load(getClass().getResource("paint.fxml"))));
        stage.setTitle("Paint App");
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Author video link: link

    
04.08.2018 / 00:58
0

The code below has been adapted from the following solution , with some changes.

private ColorPicker colorPicker;
private Button drawLine;
private Canvas canvas;
private GraphicsContext graphicsContext;

@Override
public void start(Stage primaryStage) {
    drawLine = new Button("Linha");
    colorPicker = new ColorPicker();
    colorPicker.setOnAction((ActionEvent) -> {
        graphicsContext.setStroke(colorPicker.getValue());
    });

    canvas = new Canvas(400,400);
    initCanvas(canvas);

    VBox menu = new VBox(5);
    menu.setAlignment(Pos.CENTER);
    menu.getChildren().addAll(drawLine, colorPicker);

    BorderPane root = new BorderPane();
    root.setLeft(menu);
    root.setCenter(canvas);

    Scene scene = new Scene(root, 800, 600);
    primaryStage.setScene(scene);
    primaryStage.show();
}

private void initCanvas(Canvas canvas) {
    graphicsContext = canvas.getGraphicsContext2D();
    double canvasWidth = graphicsContext.getCanvas().getWidth();
    double canvasHeight = graphicsContext.getCanvas().getHeight();

    canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, 
            new EventHandler<MouseEvent>(){

        @Override
        public void handle(MouseEvent event) {
            graphicsContext.beginPath();
            graphicsContext.moveTo(event.getX(), event.getY());
            graphicsContext.stroke();
        }
    });

    canvas.addEventHandler(MouseEvent.MOUSE_DRAGGED, 
            new EventHandler<MouseEvent>(){

        @Override
        public void handle(MouseEvent event) {
            graphicsContext.lineTo(event.getX(), event.getY());
            graphicsContext.stroke();
        }
    });

    graphicsContext.setFill(Color.LIGHTGRAY);

    graphicsContext.fill();
    graphicsContext.strokeRect(0, // x of the upper left corner
            0, // y of the upper left corner
            canvasWidth, // width of the rectangle
            canvasHeight); // height of the rectangle

    graphicsContext.setFill(Color.RED);
    graphicsContext.setStroke(Color.BLACK);
    graphicsContext.setLineWidth(1);
}

It only draws lines by default but should give you an idea of how to do the other designs. I suggest you also keep track of the memory consumption on the JavaFX canvas, it's usually high.

    
14.08.2018 / 02:59