开发者

Infinite loop when updating JPanel's graphics

So, I am drawing a graphic in a JPanel object using Grahics2D.

The JPanel is placed in a JScrollPane for the cases when my graphic is bigger than the window.

But after I draw my graphic the JPanel's size does not change and I cannot scroll to see the rest of my graphic, so I locate the lowest and most left points and set the size manually in the method that does the drawing (method is called drawTrack()).

When I switch windows or do something else that makes the JPanel to be drawn again, my graphic disappears, so I rewrote paint(), repaint(), validate(), invalidate() methods and in there I invoke the drawTrack() method to draw my graphic on every possible case of redrawing the JPanel.

The problem is that when the JPanel invokes one of the methods that do the redrawing I invoke drawTrack() in them, which after redrawing my graphic sets the size manually so that I can scroll the JScrollPane and see my whole graphic. But when I invoke setSize() method on the JPanel that makes it to be redrawn again, which means to invoke drawTrack() and so o开发者_Python百科n and so on.

The scroll bars appear because the size is correct but it creates an infinite loop that redraws my graphic over and over. And if I don't invoke the setSize() method my JPanel gets default size so it can fit in the JScrollPane's viewport and thus I cannot scroll it to see my graphic.

Any suggestions?


When you resize a JPanel you stimulate a repaint, and so if you change the size in a paint or paintComponent method or a method called in either of these methods, it makes sense to me that you are at risk of causing an infinite loop.

Solution: Don't resize anything in a paint or paintComponent method. That method is just for painting and nothing else. Instead if you want to set the size of your JPanel, override its getPreferredSize() method.

Here's a "colorful" example:

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

@SuppressWarnings("serial")
public class ImageReSize extends JPanel {
   private static final int INIT_WIDTH = 400;
   private static final int MAX_WIDTH = 1200;

   private static final int TIMER_DELAY = 20;
   protected static final int WIDTH_STEP = 5;
   private int width = INIT_WIDTH;
   private int height = INIT_WIDTH;
   private boolean growing = true;

   public ImageReSize() {
      new Timer(TIMER_DELAY, new ActionListener() {
         public void actionPerformed(ActionEvent e) {
            if (growing && width < MAX_WIDTH) {
               width += WIDTH_STEP;
               height += WIDTH_STEP;
            } else {
               growing = false;
            }

            if (!growing && width > INIT_WIDTH) {
               width -= WIDTH_STEP;
               height -= WIDTH_STEP;
            } else {
               growing = true;
            }
            // get the parent container which is the scrollpane's viewport
            JComponent parent = (JComponent)getParent();
            parent.revalidate(); // let it relayout the changed size JPanel
            parent.repaint();  // and repaint all
         }
      }).start();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D)g;
      float x = (float)width / 10;
      float y = (float)height / 10;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setPaint(new GradientPaint(x, 0, Color.green, 0, y, Color.black, true));
      g2.fillRect(0, 0, getWidth(), getHeight());
      g2.setPaint(new GradientPaint(0, 0, Color.blue, x, y, Color.red, true));
      g2.fillOval(0, 0, width, height);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(width, height);
   }

   private static void createAndShowUI() {
      ImageReSize imageReSize = new ImageReSize();
      JFrame frame = new JFrame("ImageReSize");
      frame.getContentPane().add(new JScrollPane(imageReSize));
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
         public void run() {
            createAndShowUI();
         }
      });
   }
}


so I rewrote paint(), repaint(), validate(), invalidate() methods and in there I invoke the drawTrack() method

This is unnecessary. The custom painting code should only ever be invoked from the paintComponent() method.


Faced similar problem: JPanel's paintComponent method called other "methods", that involved changes in Graphics and therefore called for another paintComponent loop.

I haven't found a structural way to move these "methods" outside paintComponent, but found the following solution: surround these "methods" with if clause and break the loop with a boolean flag:

boolean flag;

@Override
public void paintComponent(Graphics g) {

    if (flag) {
        gModify(); // Method that modifies Graphics g object and calls another paintComponent loop.
        flag = !flag; // True to false - miss gModify() in the second paintComponent entry to break the loop.
    } else {
        flag = !flag; // False to true - allow gModify() after second  paintComponent entry.
    }

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜