Search code examples
javaswingjava-2d

Java painting program: How to leave a trail behind a shape


I am trying to create a painting program, which allows you to move a shape and it paints the shape onto a JFrame. However, when I paint, it does not leave a trail. I figured that it does not leave a trail because I am overriding the paint method and that clears my previous painted object when I want to paint a new one. But when I remove the code that overrides this and run the program the shape leaves a trail behind but the buttons on the top of the frame get all messed up.

MovingBlockFrame.java:

package MovingBlock;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class MovingBlockFrame extends JFrame implements KeyListener, ActionListener, ChangeListener{


   FlowLayout flo = new FlowLayout(FlowLayout.LEADING, 10, 10);
   JPanel buttonPanel = new JPanel();
   JButton circle = new JButton("Circle");
   JButton rectangle = new JButton("Rectangle");
   JButton color = new JButton("Color");
   JSlider speed = new JSlider(0, 100, 10);

   Graphics2D test3;
   int desiredWidth = 780;
   int desiredHeight = 800;
   MovingBlockCanvas background = new MovingBlockCanvas();
   int paintSpeed = 10;
   boolean diagonalKeysDown;
    boolean diagonalKeysUp;

    public MovingBlockFrame(){


    
    setTitle("Painter");
    buttonPanel.setBorder(BorderFactory.createLineBorder(Color.black));
    setSize(desiredWidth, desiredHeight);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    
    KeyListener();
    buttonPanel.setLayout(flo);
    buttonPanel.add(circle);
    buttonPanel.add(rectangle);
    buttonPanel.add(color);
    buttonPanel.add(speed);

    

    background.desiredShape = "Circle";
    background.addKeyListener(this);

    add(background);
    add(buttonPanel, BorderLayout.NORTH);

    setVisible(true);
    
    
    
}

public static void main(String[] args){
    MovingBlockFrame frame = new MovingBlockFrame();
    
    frame.addKeyListener(frame);
}

public void keyPressed(KeyEvent input) {

    int pressed = input.getKeyCode();
    if(pressed == 83){
        diagonalKeysDown = true;
        background.xValue+= paintSpeed;
        background.repaint();
    }else if(pressed == 68 && !diagonalKeysDown){
        background.yValue+=paintSpeed;
        background.repaint();
    }else if(pressed == 87 ){
        diagonalKeysUp = true;
        background.xValue-=paintSpeed;
        background.repaint();
    }else if(pressed == 65 ){
        background.yValue-=paintSpeed;
        background.repaint();
    } if(diagonalKeysDown && pressed == 68){
        background.yValue+=paintSpeed;
        background.xValue+= paintSpeed;
        background.repaint();
    } if(diagonalKeysUp  && pressed == 68){
        background.yValue+=paintSpeed;
        background.xValue-= paintSpeed;
        background.repaint();
    } if(diagonalKeysDown && pressed == 65){
        background.yValue-=paintSpeed;
        background.xValue+= paintSpeed;
        background.repaint();
    } if(diagonalKeysUp && pressed == 65){
        background.yValue-=paintSpeed;
        background.xValue-= paintSpeed;
        background.repaint();
    }
}


public void keyReleased(KeyEvent input) {
    int released = input.getKeyCode();
    if(released == 83){
        diagonalKeysDown = false;
    } if(released == 87){
        diagonalKeysUp = false;
    }
}

public void keyTyped(KeyEvent input) {

}

public void actionPerformed(ActionEvent event) {
    String command = event.getActionCommand();
        if(command.equals("Circle")){
            background.desiredShape = "Circle";
            background.repaint();
        }else if (command.equals("Rectangle")){
            background.desiredShape = "Rectangle";
            background.repaint();
        }else if(command.equals("Color")){
            //ColorSliders test = new ColorSliders();
        }
    
}


public void stateChanged(ChangeEvent event) {
    JSlider source = (JSlider) event.getSource();
    if(source.getValueIsAdjusting() != true){
        paintSpeed = source.getValue();
    }
    
}

public void KeyListener(){
    buttonPanel.addKeyListener(this);
    circle.addKeyListener(this);
    rectangle.addKeyListener(this);
    circle.addActionListener(this);
    rectangle.addActionListener(this);
    color.addKeyListener(this);
    color.addActionListener(this);
    speed.addKeyListener(this);
    speed.addChangeListener(this);
}

}

MovingBlockCanvas.java:

package MovingBlock;

import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JPanel;

public class MovingBlockCanvas extends JPanel {

int xValue = 50;
int yValue = 50;
String desiredShape;


public MovingBlockCanvas(){

}


public void paint(Graphics render){
    
    //super.paint(render);
    
    render.setColor(ColorPanel.getColor2());
    
    if(desiredShape == "Rectangle"){
        render.fillRect(yValue, xValue, 50, 50);
    }
    if(desiredShape == "Circle"){
        render.fillOval(yValue, xValue, 50, 50);

    }   
}
}

The problem is in the paint method when I override the method I will not leave a trail for the shapes. Also if you have any coding tips I would love to hear them. Thank you in advance.


Solution

  • This is what I learned to fix this error. I just had to create a new buffered image then draw to that image, then redraw the buffered image using the paintComponent method. This is the class I created with the updated code to make the program paint and leave a trail behind the shape while still overriding the paintComponenet method.

    public class MovingBlockCanvas extends JPanel {
    
    int xValue = 50;
    int yValue = 50;
    int width = 10;
    int height = 10;
    String desiredShape;
    BufferedImage drawing;
    
    public MovingBlockCanvas(){
    
    }
    
    public void drawShapes(){
        if(drawing == null){
            createBufferedImage();
        }
        if(desiredShape.equals("Rectangle")){
            Graphics2D g = drawing.createGraphics();
            g.setColor(ColorPanel.getColor2());
            g.fillRect(yValue, xValue, width, height);
            g.dispose();
        }
        if(desiredShape.equals("Circle")){
            Graphics2D g = drawing.createGraphics();
            g.setColor(ColorPanel.getColor2());
            g.fillOval(yValue, xValue, width, height);
            g.dispose();
        }
        repaint();
    }
    
    public void createBufferedImage() {
    
    
            BufferedImage buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = buffer.createGraphics();
            g2d.setColor(Color.white);
            g2d.fillRect(0, 0, getWidth(), getHeight());
            g2d.dispose();
            drawing = buffer;
    
    }
    
    
    @Override
    public void paintComponent(Graphics render){
    
        super.paintComponent(render);
        Graphics2D g = (Graphics2D) render.create();
        if(drawing == null){
            createBufferedImage();
        }
        g.drawImage(drawing, 0, 0, this);
        g.dispose();
    
    
    }
    
    
    }
    

    Thank you to everyone that helped me learn this!