【问题标题】:Why does this this Swing bug occur when using repaint() and not with getParent().repaint()?为什么在使用 repaint() 而不是 getParent().repaint() 时会出现这个 Swing 错误?
【发布时间】:2026-02-01 22:35:01
【问题描述】:

这个问题是基于我不久前在一个简单的 Swing 骰子程序中遇到的一个问题。我发布的原始问题是 here 并且有一个可接受的答案,但我想确切地知道发生了什么,为什么会出现问题,以及为什么解决方案有效。

我设法削减了原始代码以找到问题的核心,现在它看起来非常不同:

  • 我有两个ColorPanels,每个都画一个彩色方块
  • 当您单击面板时,框应按以下顺序更改颜色:从黑色开始,然后是 >red>green>blue>red>green>blue> 等
  • 一旦盒子改变了颜色,它就不会再变黑了

但是,当我在MouseListener 中调用repaint() 时,程序的行为非常奇怪:

  • 我点击一个面板,正方形的颜色发生变化
  • 然后我点击另一个,它的正方形改变了颜色,但第一个正方形也改变了,变回黑色
  • 您可以在下面的 gif 中看到这种行为:

如果您改用getParent().repaint(),则此行为消失,程序按预期运行:

  • 问题似乎只有在面板/方块开始“重叠”时才会出现。
  • 如果您使用的布局可以阻止此操作或未将尺寸设置为较小,则似乎不会出现问题。
  • 问题并非每次都发生,这让我最初认为可能涉及并发问题。
  • 我在原始问题中遇到问题的代码似乎并没有给所有人带来问题,因此我的 IDE、jdk 等也可能是相关的:Windows 7、Eclipse Kepler、jdk1.7.0_03

代码减去导入等如下:

public class ColorPanelsWindow extends JFrame{

    static class ColorPanel extends JPanel {

        //color starts off black
        //once it is changed should never be 
        //black again
        private Color color = Color.BLACK;

        ColorPanel(){
            //add listener
            addMouseListener(new MouseAdapter(){
                @Override
                public void mousePressed(MouseEvent arg0) {
                    color = rotateColor();
                    repaint();
                    //using getParent().repaint() instead of repaint() solves the problem
                    //getParent().repaint();
                }
            });
        }
        //rotates the color black/blue > red > green > blue
        private Color rotateColor(){
            if (color==Color.BLACK || color == Color.BLUE)
                return Color.RED;
            if (color==Color.RED)
                return Color.GREEN;
            else return Color.BLUE;
        }

        @Override
        public void paintComponent(Graphics g){
            g.setColor(color);
            g.fillRect(0, 0, 100, 100);
        }
    }

    ColorPanelsWindow(){
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setLayout(new GridLayout(1,0));
        add(new ColorPanel());
        add(new ColorPanel());
        //the size must be set so that the window is too small
        // and the two ColorPanels are overlapping
        setSize(40, 40);
//      setSize(300, 200);

        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable(){

            @Override
            public void run() {
                new ColorPanelsWindow();
            }

        });
    }
}

所以我的问题是,这里到底发生了什么?

【问题讨论】:

  • 而且不会发生,只有调整大小。

标签: java swing drawing paintcomponent repaint


【解决方案1】:

但我想知道到底发生了什么,

我在 Windows 7 上使用 JDK7u60 时看到同样的问题。对我来说绝对是一个错误。

我最好的猜测是双缓冲有问题。

我在paintComponent() 方法中添加了这样的调试代码。

1) 当您单击右侧组件时,只会调用其paintComponent() 方法,并且面板会被涂上正确的颜色。

2) 当您单击左侧组件时,仅调用其 paintComponent() 方法并且面板被绘制正确的颜色,但是右侧的面板恢复为黑色,而不调用 paintComonent() 方法右侧面板。这让我相信不知何故正在使用旧缓冲区(这将是错误,我不知道如何修复它)。

getParent().repaint() 起作用的原因是,无论您单击哪个面板,这都会强制重新绘制两个组件。

【讨论】:

  • 这是有道理的。在我的原始代码中,我有一些随机事件和骰子之类的东西,并认为这可能是一个并发问题,但一个缓冲区错误可以更好地解释它。我将等待一两天,看看是否有人能提出确切的工作原理,否则我会将其标记为已接受。
【解决方案2】:

我不确定您的问题的原因,因为我无法使用您的代码重现错误行为,但是您的 paintComponent 覆盖忽略了调用 super 的 paintComponent 方法。把它放进去看看会发生什么。

    @Override  // method should be protected, not public
    protected void paintComponent(Graphics g) { 

        // ******* add
        super.paintComponent(g);

        g.setColor(color);
        g.fillRect(0, 0, 100, 100);
    }

请注意,如果这是我的程序,并且这是我想要的唯一绘画行为,我会简化我的程序,不覆盖paintComponent,而是简单地调用setBackground(color);,我还会创建一个颜色数组或列表并迭代通过它:

public static final Color[] COLORS = {Color.RED, Color.Blue, Color.Green};
private int colorIndex = 0;

// .....

@Override
public void mousePressed(MouseEvent mEvt) {
   colorIndex++;
   colorIndex %= COLORS.length;
   color = COLORS[colorIndex];
   setBackground(color);
}

我也会重写 getPreferredSize 方法。


例如,

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.*;

@SuppressWarnings("serial")
public class MyColorsPanelDemo extends JPanel {
   private static final int GAP = 20;

   public MyColorsPanelDemo() {
      setLayout(new GridLayout(1, 0, GAP, GAP));
      setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
      add(new ColorPanel());
      add(new ColorPanel());
   }

   private static class ColorPanel extends JPanel {
      public static final Color[] COLORS = {Color.red, Color.green, Color.blue};
      private static final int PREF_W = 100;
      private static final int PREF_H = PREF_W;
      private static final Color INIT_BACKGROUND = Color.black;
      private int colorIndex = 0;

      public ColorPanel() {
         setBackground(INIT_BACKGROUND);
         addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent mEvt) {
               setBackground(COLORS[colorIndex]);
               colorIndex++;
               colorIndex %= COLORS.length;
            }
         });
      }

      @Override
      public Dimension getPreferredSize() {
         return new Dimension(PREF_W, PREF_H);
      }
   }

   private static void createAndShowGui() {
      MyColorsPanelDemo mainPanel = new MyColorsPanelDemo();

      JFrame frame = new JFrame("MyColorsPanelDemo");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

【讨论】:

  • +1 用于正确的绘画技术(覆盖 getPreferredSize() 并调用 super.paintComponent())。
  • 感谢您的试用和提示。这段代码不是我用过的任何东西,但只是我得到的最小的代码,它使错误以明显的方式出现。使用合理的尺寸和getPreferredSize() 和流布局等将阻止错误出现。不幸的是,super.paintComponent(g) 并没有改变任何东西。
最近更新 更多