为什么在使用repaint()而不是getParent()时会出现这个Swing错误。重绘()?

时间:2014-06-14 18:30:53

标签: java swing drawing paintcomponent repaint

这个问题是基于我用一个简单的Swing骰子程序回来的问题。我发布的原始问题是here并且已经接受了答案,但我想确切地知道发生了什么,问题发生的原因以及解决方案的工作原理。

我成功地缩减了原始代码以找到问题的核心,现在看起来非常不同:

  • 我有两个ColorPanel,每个都画一个彩色方块
  • 当您点击面板时,该框应按此顺序更改颜色:从黑色开始,然后是>红色>绿色>蓝色>红色>绿色>蓝色>等
  • 一旦盒子改变颜色,它就不应该再次变黑了

但是当我在repaint()中拨打MouseListener时,该程序表现得非常奇怪:

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

buggy program

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

enter image description here

  • 如果面板/正方形开始“重叠”,则似乎只会出现问题。
  • 如果使用停止此布局或不将尺寸设置得小的布局,则似乎不会出现问题。
  • 每次都不会发生这个问题,最初让我觉得可能会涉及到并发问题。
  • 我在原始问题中遇到问题的代码似乎并没有给每个人带来问题,所以我的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();
            }

        });
    }
}

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

2 个答案:

答案 0 :(得分:4)

  

但我想确切知道发生了什么,

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

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

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

1)当您单击右侧组件时,仅调用其paintComponent()方法,并且该面板被绘制为正确的颜色。

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

paintComonent()的工作原因是因为无论您点击哪个面板,都会强制重新绘制两个组件。

答案 1 :(得分:3)

我不确定您的问题的原因,因为我无法使用您的代码重现错误行为,但您的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();
         }
      });
   }
}