Search code examples
javaswingmouseeventpaintcomponentrepaint

How to stop JComponent from clearing?


I am making a molecule designing application. I can draw the lines and circles, but it clears the old lines each time you click, so basically, you can only design molecules with 2 atoms. Also, the mouseEvents don't deliver if you click very fast which is also a problem. Here is the code:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;
import javax.swing.JFrame;
public class MoleculeDesigner extends JComponent implements MouseListener {
    private Point op, cp;
    private boolean first = true;
    public static final Color linecolor = new Color(0, 255, 0);
    private static final long serialVersionUID = 1L;
    private BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
    public MoleculeDesigner() {
        JFrame f = new JFrame("Molecule Designer");
        f.setBackground(Color.WHITE);
        f.addMouseListener(this);
        f.add(this);
        f.setSize(100, 100);
        f.setDefaultCloseOperation(3);
        f.setVisible(true);
    }
    public static void main(String[] args) {
        new MoleculeDesigner();
    }
    @Override
    protected void paintComponent(Graphics g) {
        if(op != null && cp != null) {
            Graphics2D g2 = img.createGraphics();
            super.paintComponent(g2);
            g2.setColor(linecolor);
            g2.drawLine((int) op.getX(), (int) op.getY(), (int) cp.getX(), (int) cp.getY());
            g2.setColor(Color.BLACK);
            g2.fillOval((int) cp.getX(), (int) cp.getY(), 10, 10);
            op = (Point) cp.clone();
            g2.dispose();
        }
    }
    @Override
    public Dimension getPreferredSize() {
        return getParent().getMaximumSize();
    }
    @Override
    public void mouseClicked(MouseEvent e) {
        if(!first) {
            cp = e.getPoint();
            cp.setLocation(cp.getX(), cp.getY() - 8);
        }
        else {
            op = e.getPoint();
            first = false;
        }
        repaint();
    }
    @Override public void mousePressed(MouseEvent e) {}
    @Override public void mouseReleased(MouseEvent e) {}
    @Override public void mouseEntered(MouseEvent e) {}
    @Override public void mouseExited(MouseEvent e) {}
}

All help appreciated!


Solution

  • Either 1) draw in a BufferedImage which is then displayed inside of your paintComponent override, or 2) put your data into an ArrayList or other collection, and then iterate through the collection inside of paintComponent. I'd do the latter if I needed the data for other purposes. Also, never ever do this:

    public void update(Graphics g) {
        paintComponent(g);
    }
    

    This is not how Swing graphics are supposed to be done and is potentially dangerous code. Please read:


    Edit
    More detail regarding option 1:

    • Create a BufferedImage using one of its constructors.
    • Do your drawing on the image.
      • When you need to draw, get a Graphics object from the BufferedImage using getGraphics() or createGrahpics() (for a Graphics2D object)
      • Draw with this Graphics object
      • Then dispose() the Graphics object.
    • Then call repaint() to ask the JVM to repaint the component.
    • Draw the image in your paintComponent method by calling g.drawImage(...), passing in your buffered image.

    Benefits: often the drawing is quicker, and I often use this to draw background images.
    Drawbacks: the data points are not available, and so if you need to do manipulation or animation of your data points, this is not the way to go.