Hi, I'm trying to write a graphing application, that takes an equation and graphs it, and thats no problem. However, I want to add a cursor, which moves along with the mouse. I managed to add one, however, the cursor causes everything else to refresh too. Is there anyway to move just the cursor, keep the other contents from getting refreshed?

I've included an example of my problem in the provided source code. In example, the line in the background refreshes with the cursor, because it is in one paint() method. How can I make the background independent from the cursor?

Thanks.

Attachments
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

public class Grapher extends JFrame
{
	private int
			width = 800,
			height = 600;

	public Grapher()
	{
		//Create the graphWriter
		graphWriter graph = new graphWriter();

		getContentPane().add(graph, BorderLayout.CENTER);

		setSize(width, height);
		setVisible(true);
		setTitle("Grapher");
		setResizable(false);
	}

	public static void main(String args[])
	{
		Grapher window = new Grapher();

		window.addWindowListener(
			new WindowAdapter()
			{
				public void windowClosing(WindowEvent e)
				{
					System.exit(0);
				}
			}
		);
	}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class graphWriter extends JPanel
{
	private int
			markerX = 0,
			markerY = 0,
			width = 0,
			height = 0;

	public graphWriter()
	{
		addMouseMotionListener(
			new MouseMotionListener()
			{
				public void mouseDragged(MouseEvent e)
				{}

				public void mouseMoved(MouseEvent e)
				{
					//Get coordinates of the mouse
					markerX = e.getX();
					markerY = e.getY();
					repaint();
				}
			}
		);
	}

	public void paint(Graphics g)
	{
		super.paint(g);
		//Get width and height of current clip
		width  = (int)g.getClipBounds().getWidth();
		height = (int)(g.getClipBounds().getHeight());

		//Draw line
		g.drawLine(0, 0, width, height);

		//Draw two lines, one horizontal, and one vertical to create the cursor
		g.drawLine(markerX, 0, markerX, height);
		g.drawLine(0, markerY, width, markerY);
	}
}

You really don't need to. Just be sure that you are doing expensive calculations and such outside of the paint() method and only painting the results. Paint will be called any time the OS needs to update the screen (ie moving, resizing, etc) so try to keep it as lean as possible. If you want to play with buffered image painting, that is one way to keep a copy of what you have already drawn and then draw over it as needed, but for your small example I don't think you need to do that.

You may also want to consider overriding paintComponent() of your JPanel instead of paint(), since paint also paints the borders and child components. paintComponent() is more narrowed to the area you are wanting to render.

So would creating a buffered image and painting the cursor on top of that be like creating a layer on top of an image in Photoshop, and drawing on the new layer?

Thanks.

So would creating a buffered image and painting the cursor on top of that be like creating a layer on top of an image in Photoshop, and drawing on the new layer?

Thanks.

Yes, somewhat like that. It allows you to draw to an offscreen image and then render that image to the screen. In your case the line would be the image and your paint code would drawImage() and then draw your lines.
http://java.sun.com/docs/books/tutorial/2d/images/index.html

In your case though, I don't think you will really gain anything with the extra trouble. Keeping a BufferedImage offscreen is helpful if you have a very complex image that you don't want to have the expense of regenerating with each paint. Your line drawing doesn't really have that issue here.

This would be your code with an offscreen image for the line

public class graphWriter extends JPanel {
    private int
      markerX = 0,
      markerY = 0,
      width = 0,
      height = 0;
    
    Image bufImage = null;
    
    public graphWriter() {
        addMouseMotionListener(
          new MouseMotionListener() {
            public void mouseDragged(MouseEvent e) {
            }
            
            public void mouseMoved(MouseEvent e) {
                //Get coordinates of the mouse
                markerX = e.getX();
                markerY = e.getY();
                repaint();
            }
        }
        );        
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (bufImage==null){
            bufImage = createImage(getWidth(), getHeight());
            Graphics gBuf = bufImage.getGraphics();
            //Draw line
            gBuf.drawLine(0, 0, getWidth(), getHeight());
        }

        //Get width and height of current clip
        width  = (int)g.getClipBounds().getWidth();
        height = (int)(g.getClipBounds().getHeight());
        
        //Draw line
        g.drawImage(bufImage,0,0,this);
        
        //Draw two lines, one horizontal, and one vertical to create the cursor
        g.drawLine(markerX, 0, markerX, height);
        g.drawLine(0, markerY, width, markerY);
    }
}

Well, im hoping to apply that logic to another program (picture), and i think it would be much more efficient if only the cursor were redrawn, and the rest were left alone. As it is right now, each stock price is an object, and they all have to be redrawn along with the bands, and the volume, and everything else.

Attachments actual.png 55.3 KB concept.png 31.17 KB

Well, im hoping to apply that logic to another program (picture), and i think it would be much more efficient if only the cursor were redrawn, and the rest were left alone. As it is right now, each stock price is an object, and they all have to be redrawn along with the bands, and the volume, and everything else.

Ah, ok, yes that is a bit more to render. The offscreen image may work pretty well for you then. For complex lines, you may also want to look at using drawPolyline() from prebuilt x[] and y[] coord arrays. That made a big difference for us on a few of our many-lined graphs here.

This article has been dead for over six months. Start a new discussion instead.