Question

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!

Was it helpful?

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.

OTHER TIPS

You don't, nor should you.

paint in Swing is a destructive process, this is the way it was designed. That is, there is an expectation that when you component is requested to paint itself, it will clean up the Graphics context before painting anything (this is slightly different for transparent components though).

Swing has no concept of what was painted on your component before and because the Graphics context is shared amongst all the components been painted, unless you clear the graphics first, you could end up with unwanted paint artifacts

Possible solutions might include...

  1. Painting to some kind of backing buffer (such as a BufferedImage), which you use the paintComponent method to draw. This is limited in the fact that it just acts like a paint program, painting pixels to the image. You will also need to provide functionality when the size of the viewable area changes, as the BufferedImage won't know.
  2. Place each object you wanted painted into some kind of List and iterate this list when paintComponent is called. This is a little more flexible in that you can control the order of the objects drawn, remove objects and insert new ones where you like
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top