javaswingmousepen

Drawing pen strokes using java swing...strokes don't show up


So I have two classes here:

PhotoComponent class:

(This class is to handle a specific image as a JComponent. When "flipped" I want to draw pen strokes instead of having an image. So I replace the image with a rectangle, attempting to draw pen strokes over it.)

public class PhotoComponent extends JComponent {

private Image pic;
private boolean flipped;

private int contentAreaWidth;
private int contentAreaHeight;
p
@Override
public void paintComponent(Graphics g) {
    //Does all the drawing and contains whatever state information is associated with the photo
    //create an action event to auto call repaint
    //call repaint anytime flip was changed to true or false

    System.out.println("Draw: " + draw + ", Pic: " + pic);
    if (draw && pic != null) {
        super.paintComponent(g);

        System.out.println("width using this: " + this.getWidth() + ", actual width of JPanel: " + contentAreaWidth);
        System.out.println("height using this: " + this.getHeight() + ", actual height of JPanel: " + contentAreaHeight);

        g2 = (Graphics2D) g;
        int x = (contentAreaWidth - pic.getWidth(null)) / 2;
        int y = (contentAreaHeight - pic.getHeight(null)) / 2;
        if (!flipped) {
            g2.drawImage(pic, x, y, null);

        } else if (flipped) {
            g2.setColor(Color.WHITE);
            g2.fillRect(x,y,pic.getWidth(null), pic.getHeight(null));
            g2.drawRect(x, y, pic.getWidth(null), pic.getHeight(null));
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            if (drawingMode) {
                g2.setPaint(Color.RED);
                if (drawOval) {
                    penStrokes.put(ovalX, ovalY);
                    if (penStrokes != null) {
                        for (Integer xCoor : penStrokes.keySet()) {
                            g2.setPaint(Color.RED);
                            int brushSize = 5;
                            g2.fillOval((xCoor - (brushSize / 2)), (penStrokes.get(xCoor) - (brushSize / 2)), brushSize, brushSize);
                            //System.out.println("SIZE OF HASHTABLE: " + penStrokes.size());
                        }
                    }
                    System.out.println("Filling an oval!" + ovalX + ", " + ovalY);
                }
            } else if (textMode) {
                g2.setPaint(Color.YELLOW);
                if (drawRect) {
                    rectDimensions.add(rectX);
                    rectDimensions.add(rectY);
                    rectDimensions.add(rectWidth);
                    rectDimensions.add(rectHeight);

                    for (int i = 0; i < rectDimensions.size(); i+=4) {
                        g2.fillRect(rectDimensions.get(i), rectDimensions.get(i+1), rectDimensions.get(i+2), rectDimensions.get(i+3));
                        g2.drawRect(rectDimensions.get(i), rectDimensions.get(i+1), rectDimensions.get(i+2), rectDimensions.get(i+3));
                    }
                }
            }

            System.out.println("This is being called again!");
        }

    }

}
public void setRectangle(int x, int y, int width, int height) {
    drawRect = true;
    rectX = x;
    rectY = y;
    rectWidth = width;
    rectHeight = height;
}

public void removeRectangle() {
    drawRect = false;
}

public int[] setOval(int currentX, int currentY) {
    drawOval = true;
    int[] toReturn = {ovalX, ovalY};
    ovalX = 

NOTE THE DRAWLINE() METHOD ABOVE. I am drawing at the given points, repainting, and setting the old variables to be the current variables.

Main class:

private static PhotoComponent img;
private static JFrame frame;

private static JPanel contentArea;

//Mouse Coordinates for PenStrokes
private static int oldX, oldY;

//Mouse Coordinates for Sticky Notes
private static Point clickPoint;

public static void main (String[] args) {
    frame = new JFrame("PhotoManip");
    img = null;
    contentArea = null;
    oldX = 0;
    oldY = 0;
    setupMenubar(frame);
    setupJFrame(frame);
}

private static void addPhotoComponent(File file) {

            }
            if (img.getTextMode()) {
                img.removeRectangle();
                clickPoint = null;
            }

        }
    });

    img.addMouseMotionListener(new MouseAdapter() {
        @Override
        public void mouseDragged(MouseEvent e) {
            if (img.getDrawingMode()) {
                if (withinRange(e.getX(), e.getY())) {
                    int[] toUpdate = img.setOval(e.getX(), e.getY());
                    oldX = toUpdate[0];
                    oldY = toUpdate[1];
                    img.repaint();
                }
            }
            if (img.getTextMode()) {
                if (withinRange(e.getX(), e.getY())) {
                    Point dragPoint = e.getPoint();
                h, height);
                    img.repaint();
                }
            }

        }
    });

    if (img!=null) {
        contentArea.add(img);
    }
}

private static boolean withinRange(int x, int y) {
    if (x > img.getX() && x < img.getX() + img.getWidth()) {
        if (y > img.getY() && y < img.getY() + img.getHeight()) {
            return true;
        }
    }
    return false;
}

private static void flipImage() {
    if (!img.isFlipped()) {
        img.setFlipped(true);
    } else if (img.isFlipped()) {
        img.setFlipped(false);
    }
}

drawLine() is called above in this main class, when a mousedrag occurs. Problem is that the strokes don't appear to show.

I know that the program is calling g2.fillOval() because I am printing out a verification statement afterwards.

Additionally, I have created print statements for when the mouse is pressed and dragged and they are getting the correct coordinates?

Why don't red strokes appear? I'm confused. Is it the way my code is structured?


Solution

  • The crux of your problem is that you are trying to draw something outside the paintComponent method, which is never supported. Whatever you draw will get overwritten by the next call of paintComponent, which will happen almost instantly. We can solve this by storing the co-ordinates of the oval and drawing it within paintComponent instead of trying to draw on a graphics object outside of the paintComponent method. See code below:

    First we are going to add the following variables to your PhotoComponent class:

    private boolean drawOval = false;
    private int ovalX = 0;
    private int ovalY = 0;
    

    Then we will add methods for controlling them:

    public int[] setOval(int currentX, int currentY) {
        drawOval = true;
        int[] toReturn = {ovalX, ovalY};
        ovalX = currentX;
        ovalY = currentY;
        return toReturn;
    }
    
    public void removeOval() {
        drawOval = false;
    }
    

    After that we can change the paintComponent method to have it draw the oval based on those variables:

    @Override
    public void paintComponent(Graphics g) {
        //Does all the drawing and contains whatever state information is associated with the photo
        //create an action event to auto call repaint
        //call repaint anytime flip was changed to true or false
        super.paintComponent(g);
        g2 = (Graphics2D) g;
        int x = (contentAreaWidth - pic.getWidth(null)) / 2;
        int y = (contentAreaHeight - pic.getHeight(null)) / 2;
        if (!flipped) {
            g2.drawImage(pic, x, y, null);
    
        } else if (flipped) {
            g2.setColor(Color.WHITE);
            g2.fillRect(x, y, pic.getWidth(null), pic.getHeight(null));
            g2.drawRect(x, y, pic.getWidth(null), pic.getHeight(null));
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setPaint(Color.RED);
        }
    
        //took the code you already used for drawing the oval and moved it here
        if (drawOval) {
            g2.setPaint(Color.RED);
            int brushSize = 5;
            g2.fillOval((ovalX - (brushSize / 2)), (ovalY - (brushSize / 2)), brushSize, brushSize);
        }
    }
    

    Finally change the addPhotoComponent method to update those variables instead of trying to draw the oval directly:

    private static void addPhotoComponent(File file) {
        Image image = null;
        try {
            image = ImageIO.read(file);
        } catch (IOException e2) {
            System.out.println("ERROR: Couldn't get image.");
        }
    
        img = new PhotoComponent(image, contentArea);
        img.revalidate();
    
        img.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    // your code here
                    System.out.println("You flipped the photo!!!");
                    flipImage();
                    img.repaint();
                }
            }
    
            @Override
            public void mousePressed(MouseEvent e) {
                img.setOval(e.getX(), e.getY());
            }
            @Override
            public void mouseReleased(MouseEvent e) {
                img.removeOval();
            }
        });
    
        img.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                int[] toUpdate = img.setOval(e.getX(), e.getY());
                oldX = toUpdate[0];
                oldY = toUpdate[1];
            }
        });
    
        if (img != null) {
            contentArea.add(img);
        }
    }