Preventing a JPanel from repainting

I know that there are many, many posts on these forums saying that repainting is a good thing for components. I have to agree with many of them. However, I have a situation where repainting a JPanel seems overkill.

I have built an applet that will read some values from a pair of files and will draw a graph from these values on a class that extends JPanel. However,due to the length of the files, the graph (and panel) are much, much larger than the size of the applet. Therefore, I am using a JScrollPane to containmy panel. The graph is drawn onto the JPanel using Graphics commands, but none of the data changes after the values are read in from the files. I amusing the JScrollPane to show the entire graph. However, whenever the JScrollPane is scrolled, the JPanel is needlessly redrawn; all the data is the same. All I want JScrollPane to do is expose different parts of the graph based on where the pane is scrolled to. Is it possible to have the data be drawn on the JPanel once (at the beginning) and to never have to draw it again by finalizing the shapes drawn on it?

Some code:

publicclass AppletOfMyEyeextends javax.swing.JApplet{

privateclass GraphPaneextends JPanelimplements Scrollable{

int graphPane1Begin;

int graphPane2Begin;

int graphPane1End;

int graphPane2End;

public GraphPane()

{

super(true);

setAutoscrolls(true);

addMouseMotionListener(this);

}

public Dimension getPreferredScrollableViewportSize()

{

return getPreferredSize();

}

publicint getScrollableBlockIncrement(Rectangle visibleRect,int orientation,int direction)

{

if(orientation == SwingConstants.VERTICAL)

return 0;

else

return 125;

}

publicboolean getScrollableTracksViewportHeight()

{

returntrue;

}

publicboolean getScrollableTracksViewportWidth()

{

returnfalse;

}

publicint getScrollableUnitIncrement(Rectangle visibleRect,int orientation,int direction)

{

if(orientation == SwingConstants.VERTICAL)

return 0;

else

return 25;

}

publicvoid paint(Graphics g)

{

int width = getWidth();

int horizontalLineEnd = width - 1;

graphPane1Begin = 0;

graphPane2Begin = graphHeight+30;

graphPane1End = graphHeight;

graphPane2End = graphPane2Begin + graphHeight;

//drawing vertical minor lines for graphs 1 and 2

for(int x = 0; x <= width; x += pBMiL)

{

g.drawLine(x, graphPane1Begin, x, graphPane1End);

g.drawLine(x, graphPane2Begin, x, graphPane2End);

}

//drawing horizontal minor lines

for(int y = graphPane1Begin; y <= graphPane1End; y += pBMiL)//for graph 1

g.drawLine(0, y, horizontalLineEnd, y);

for(int y = graphPane2Begin; y <= graphPane2End; y += pBMiL)//and graph 2

g.drawLine(0, y, horizontalLineEnd, y);

}

}

publicvoid init(){

setBackground(Color.white);

totalSamples = 20000;

APPLET_WIDTH = 1001;

APPLET_HEIGHT = 726;

resize(APPLET_WIDTH,APPLET_HEIGHT);

pBML = 25;

pBMiL = 5;

font =new Font("system", 0, 16);

scrFactor = 1.0F;

graphPane =new GraphPane();

graphView =new JScrollPane();

graphView.setBackground(new java.awt.Color(255, 255, 255));

graphView.setBorder(null);

graphView.setDoubleBuffered(true);

graphView.setPreferredSize(new java.awt.Dimension(957, 672));

graphPane.setPreferredSize(new Dimension(totalSamples, graphView.getHeight()));

graphView.setViewportView(graphPane);

}

publicvoid paint(Graphics g)

{

graphView.revalidate();

graphView.repaint();

}

privateint APPLET_WIDTH, APPLET_HEIGHT, pBML, pBMiL,totalSamples;

privatefloat scrFactor;

private Font font;

private GraphPane graphPane;

private JScrollPane graphView;

}

Everytime graphView scrolls, all of graphPane is redrawn. As the number of samples goes up (20000 is not an unusual value for totalSamples), this makes scrolling not so smooth. Is there a way to execute the paint function in graphPane once, but have it always visible?

Thanks

[7426 byte] By [geffdea] at [2007-11-27 5:56:38]
# 1

1) Don't override the paint() method of JApplet. There is no need to do this.

2) Don't override the paint method of JPanel. Custom painting should be done in the paintComponent() method.

The solution to your problem is to create a BufferedImage and do the painting on the BufferedImage. Then your panel simply uses the Graphics.drawImage(...) method to draw the image.

camickra at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...
# 2
Will renaming paint(Graphics g) to paintComponent(Graphics g) satisfy #2?And when working with a BufferedImage, when should I draw it? Do I draw in it just as I am drawing on the JPanel? And is Graphics.drawImage(myBufferedImage) called in paintComponent?Thanks a lot!
geffdea at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...
# 3

> And when working with a BufferedImage, when should I draw it?

Well you said you only want to draw the image once, so you would probably create the image in the constructor of the class so that it is only created once.

> Will renaming paint(Graphics g) to paintComponent(Graphics g) satisfy #2?

No, because the code changes. Your current code would probably be moved to the constructor as suggested above. And the new code in the paintComponent(...) method would then just be something like:

super.paintComponent(g);

g.drawImage(...);

camickra at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...
# 4

I tried using the BufferedImage, but it came out more like a BuggeredImage...my graph no longer displayed properly, no matter what I did. However, I did manage to achieve my primary goal of making scrolling and repainting smooth by setting the viewport scroll mode to JViewport.BACKINGSTORE_SCROLL_MODE instead of the default BLIT_SCROLL_MODE.

Thanks for all your help.

Geoff

geffdea at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...
# 5

Then you are doing something wrong. The Graphics object of the buffered image is the same as the Graphics object of your cusomt component. The graphing commands are exactly the same. You should learn to use the proper technique because it is far more efficient.

Here is a simple example that does some dynamic painting

http://forum.java.sun.com/thread.jspa?forumID=57&threadID=607073

camickra at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...
# 6

camickr,

I figured out what was wrong. Setting the background doesn't actually paint the background of a BufferedImage that color; I used fillRect instead. However, I have a question.

Using a BufferedImage writes a bunch of pixels and stuff to the image, which has to be stored in memory; when the BufferedImage is drawn, its pixels are copied to the graphics buffer, correct? How is this different than using the "BACKINGSTORE" scroll mode in a JViewport? From reading up on the subject, it appears that the "backing store" is just a temporary image that holds all the pixels that were previously drawn so that the JViewport can call those up instead of redrawing the entire image. How is this less efficient that drawing a BufferedImage every time I repaint? I guess, I just really want to understand what exactly that backing store is doing because, to my current understanding, the backing store does the same thing as a BufferedImage.

Thanks,

geffde

geffdea at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...
# 7

I've never used backingstore mode, but I think there is still a difference in that this mode will only be used for scrolling purposes. So from a scrolling perspective it sounds the same to me.

However, the original problem was from all your painting code being executed every time the paintComponent() method of the panel is invoked. Remember paint component needs to be invoked every time the frame is moved or every time you minimize/maximize the frame. In other words whenever the JVM determines a repaint of the panel component needs to be done the paintComponent() method will be invoked. Using backingstore mode in a viewport will not prevent the paintComponent() method from being invoked in the above situations.

camickra at 2007-7-12 16:27:35 > top of Java-index,Desktop,Core GUI APIs...