Why this Swing error occurs when using repaint () and not with getParent (). Repaint ()?

This question is based on a problem that I encountered with a simple Swing program. The original question I posted here has an accepted answer, but I would like to know exactly what is happening, why the problem is, and why the solution works.

I managed to remove the source code to understand the essence of the problem, and now it looks completely different:

  • I have two ColorPanel , each of which draws a colored square
  • When you click on the panel, the box should change color in this order: start with black, then> red> green> blue> red> green> blue> etc.
  • After the field has changed color, it should never be black.

However, when I just call repaint() in the MouseListener , the program behaves very strange:

  • I click on one panel and change the square color.
  • Then I click on another, and the square changes color, but the first square also changes, back to black
  • you can see this behavior in gif below:

buggy program

If you use getParent().repaint() , this behavior will disappear and the program will behave as expected:

enter image description here

  • The problem only occurs if the panels / squares begin to β€œoverlap”.
  • If you use a layout that stops this or does not set the size, then the problem does not occur.
  • the problem does not occur every time that initially made me think that there might be problems with concurrency.
  • The code I had problems with in my original question did not seem to cause problems for everyone, and therefore my IDE, jdk, etc. may matter: Windows 7, Eclipse Kepler, jdk1.7.0_03

Code minus import, etc. as follows:

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

So my question is: what is going on here?

+3
source share
2 answers

but I would like to know exactly what is happening,

I see the same problems as when using the JDK7u60 on Windows 7. Definitely seems a mistake to me.

My best guess is that this is a double buffering problem.

I have added debugging code to the paintComponent() method.

1) When you click on the right component, only its paintComponent() method is called and the panel is painted with the corresponding color.

2) When you click on the left component, only its paintComponent() method is called and the panel is painted with the corresponding color, however, the panel on the right goes back to black without calling the paintComonent() method on the right panel. This makes me think that some kind of old buffer is being used (it will be a mistake, and I have no idea how to fix it).

The reason getParent().repaint() works is because it forces both components to repaint regardless of which panel you click on.

+4
source

I am not sure about the cause of your problem, since I cannot reproduce the wrong behavior with your code , but your paintComponent override neglects to call the super paintComponent method. Paste this and see what happens.

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

Please note that if this was my program, and this was the only behavior I recommended, I would simplify my program by not overriding paintComponent, but simply by calling setBackground(color); , I would also create an array of Color or List and iterate through it:

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

I would also override the getPreferredSize method.


eg.

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

All Articles