更新 JPanel 图形时出现无限循环

发布于 2024-12-06 12:50:07 字数 823 浏览 1 评论 0原文

因此,我使用 Grahics2DJPanel 对象中绘制图形。
当我的图形大于窗口时,JPanel 被放置在 JScrollPane 中。
但是在绘制图形后,JPanel 的大小没有改变,并且我无法滚动查看图形的其余部分,因此我找到最低点和最左边的点,并在方法中手动设置大小进行绘图(方法称为 drawTrack())。
当我切换窗口或执行其他操作使 JPanel 再次绘制时,我的图形消失,因此我重写了 paint()repaint()validate ()invalidate() 方法,并在其中调用 drawTrack() 方法在重绘 JPanel 的每种可能情况下绘制图形。
问题是,当 JPanel 调用执行重绘的方法之一时,我会在其中调用 drawTrack() ,在重绘图形后手动设置大小,以便我可以滚动 JScrollPane 并查看我的整个图形。但是当我在 JPanel 上调用 setSize() 方法时,它会再次重绘,这意味着调用 drawTrack() 等等。
出现滚动条是因为尺寸正确,但它会创建一个无限循环,一遍又一遍地重绘我的图形。如果我不调用 setSize() 方法,我的 JPanel 将获得默认大小,以便它可以适合 JScrollPane 的视口,从而我无法滚动查看我的图形。
有什么建议吗?

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 on 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

心安伴我暖 2024-12-13 12:50:08

当您调整 JPanel 的大小时,您会刺激重新绘制,因此,如果您更改 Paint 或 PaintComponent 方法或在这两种方法中调用的方法中的大小,对我来说,您有导致无限循环的风险,这是有道理的。

解决方案:不要在paint或paintComponent方法中调整任何内容的大小。这个方法只是为了绘画而已。相反,如果您想设置 JPanel 的大小,请重写其 getPreferredSize() 方法。

这是一个“多彩”的例子:

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();
         }
      });
   }
}

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();
         }
      });
   }
}
呢古 2024-12-13 12:50:08

所以我重写了paint()、repaint()、validate()、invalidate()方法,并在其中调用了drawTrack()方法

这是不必要的。自定义绘画代码只能从paintComponent() 方法中调用。

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.

污味仙女 2024-12-13 12:50:08

面临类似的问题:JPanel 的paintComponent 方法调用了其他“方法”,涉及Graphics 的更改,因此调用另一个paintComponent 循环。

我还没有找到将这些“方法”移到 PaintComponent 之外的结构方法,但找到了以下解决方案:用 if 子句包围这些“方法”,并用布尔标志打破循环:

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.
    }

}

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.
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文