Mystery (concurrency / component drawing?) Error in a very simple Swing Dice program

Sorry for the poor title of the question, I am at a standstill about the cause of this error and do not know how to formulate this question.

I am learning basic Swing and doing this exercise from the online book Introduction to Java Programming.

I did not follow the instructions for the letter and instead try to do this:

  • there is a window that shows a visual representation of two cubes
  • when you click on one of the dice, it "rolls" and shows a new value

My implementation:

  • a very simple JDie object that extends JPanel
  • overridden paintComponent method for painting matrix views
  • change the color of the matrix every time the value changes only for visual cues
  • added a listener to “roll” the dice when the mouse is pressed and then
    repaint

Bug fixed:

  • Run the main DieTest method
  • Resize the window to fit the two stamps.
  • Click on the second die to collapse it.
  • Now click on the first die to collapse it
  • The second die value returns to its original value.
  • If you resize the window, the second matrix value will also change.

If I click on the cubes to collapse them, until I resize the window, the error will not appear ...

I suppose I'm making a basic mistake somewhere that just disguised itself as this weird behavior.

I tried to destroy the code as hard as I could, it took a long time when an error occurred and when it did not:

import java.awt.*; import java.awt.event.*; import javax.swing.*; class JDie extends JPanel { private Color color; private int value; JDie(){ value = getValue(); color = Color.BLACK; //add listener addMouseListener(new MouseAdapter(){ @Override public void mousePressed(MouseEvent arg0) { value = getValue(); //'roll' the die repaint(); } }); } /*private helper methods */ private int getValue(){ int v =(int)(Math.random()*6) + 1; //change color just to show that the //value has changed color = getRandomColor(); return v; } private Color getRandomColor(){ float r = (float)Math.random(); float g = (float)Math.random(); float b = (float)Math.random(); return new Color(r, g, b); } //draws the pips for the die @Override public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor(color); //draw pips //set pip size int pip_side = 10; switch(value){ case 1: g.fillRect(3*pip_side, 3*pip_side, pip_side, pip_side); break; case 2: g.fillRect(5*pip_side, pip_side, pip_side, pip_side); g.fillRect(pip_side, 5*pip_side, pip_side, pip_side); break; case 3: g.fillRect(5*pip_side, pip_side, pip_side, pip_side); g.fillRect(pip_side, 5*pip_side, pip_side, pip_side); g.fillRect(3*pip_side, 3*pip_side, pip_side, pip_side); break; case 4: g.fillRect(pip_side, pip_side, pip_side, pip_side); g.fillRect(5*pip_side, 5*pip_side, pip_side, pip_side); g.fillRect(5*pip_side, pip_side, pip_side, pip_side); g.fillRect(pip_side, 5*pip_side, pip_side, pip_side); break; case 5: g.fillRect(pip_side, pip_side, pip_side, pip_side); g.fillRect(5*pip_side, 5*pip_side, pip_side, pip_side); g.fillRect(5*pip_side, pip_side, pip_side, pip_side); g.fillRect(pip_side, 5*pip_side, pip_side, pip_side); g.fillRect(3*pip_side, 3*pip_side, pip_side, pip_side); break; case 6: g.fillRect(pip_side, pip_side, pip_side, pip_side); g.fillRect(5*pip_side, 5*pip_side, pip_side, pip_side); g.fillRect(5*pip_side, pip_side, pip_side, pip_side); g.fillRect(pip_side, 5*pip_side, pip_side, pip_side); g.fillRect(pip_side, 3*pip_side, pip_side, pip_side); g.fillRect(5*pip_side, 3*pip_side, pip_side, pip_side); break; } } } public class DieTest extends JFrame{ DieTest(){ setLayout(new GridLayout()); add(new JDie()); add(new JDie()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //if I set the size smaller than the JDie is displaying //and resize the window before 'rolling' the dice //then the bug appears...?! setSize(80, 80); //setting the size larger than both JDie //and it works fine whether you resize or not // setSize(200, 200); setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable(){ @Override public void run() { new DieTest(); } }); } } 

-------------- EDIT -----------------

Running the code again, I noticed that this does not happen in 100% of cases, but the error still exists. Here's the gif I just took from it, this might show the problem better:

Strange bug

You can clearly see the original value when the original color is redrawn, when I click on the first stamp again. When I resize the window, the second matrix value goes back to the previous value it had ... I really don't understand this ...

--------------- EDIT 2 ---------------------

  • I tried the same code on another computer (mac), but could not replicate the problem.
  • Compiled and ran the code on my computer outside of Eclipse, could not replicate the problem.
  • Edit the code that Eclipse compiled from the command line, only received the problem once, could not reproduce it today.
  • Having run the code from Eclipse, am I still having a problem with 1 5-10 times? If he does not appear on the first "passage", as it were, does not appear at all. Gif illustrates this well.

So, it seems that my computer is configured and affected, details:

  • Windows 7 64 bit
  • Eclipse Kepler Service Release 1
  • Java Update 7 51
  • Java SE Development Kit 7 Update 3 (64 bit)

This is complicated because I know that I know longer if it calls my code or some other program. Like newb, how can I find out if any future problems are the result of my poor programming or something else ... disappointment.

---------- EDIT 3 -----------

Like a quick exploration of the sides of concurrency: I set all instance fields to volatile I set all the methods, including paintComponent, for synchronization I deleted the Math.random () call (although I read another thread that said it was a stream implementation) and instead replaced an instance of random objects

Unfortunately, I still get a visual outage.

Another thing that I noticed is that it seems to occur much less frequently about 1 time in 10 times. I continue to hope that this is fixed, and then retry again. In my original program that I worked on, it was like 1 out of 3 (I completely changed this program, so I don't have it in my hands).

-------- EDIT 4 --------- I came up with a slightly more truncated version that no longer uses any random values ​​and still produces a visual shutdown. This seems to be happening more often with this code:

 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ColorPanelsWindow extends JFrame{ 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 //click on panel to rotate color addMouseListener(new MouseAdapter(){ @Override public void mousePressed(MouseEvent arg0) { color = rotateColor(); 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(); } }); } } 

A few observations:

  • afaict panels must first overlap.
  • if I use the layout of the stream, increase the size of the window, or in any way that first overlaps the overlap panels, then I don’t see to get the problem (or maybe it happens much less often?)
+6
source share
2 answers

Yes, yes, I know exactly what you're talking about. I also suffered from my use.

Although it was very difficult for me to find out the reason that I - even now - am not sure if this is really what I think; what happens (too technical and not suitable to publish everything here) I fixed it and I don’t even know why it works.

So, instead of repainting the component itself, redraw its container (or container container - if it is not already fixed).

 getParent().repaint(); 

Hope this helps.

+2
source

Like Moon, I can’t reproduce your results, but I have a few comments:

  • Override getPreferredSize() to determine the initial geometry.

  • Define constants that enforce geometric relationships.

  • Use a layout that takes into account the preferred size, for example. FlowLayout to see the effect.

  • Use Color.getHSBColor() to get various saturated colors.

  • Use one instance of Random if necessary.

Application: The intermittent nature and variability of the problem platform strongly suggests improper synchronization. In the original program, both bones share one static instance of Random , owned by Math ; in the example below, each stamp has its own instance. Also note that resizing the frame indirectly calls paintComponent() .

image

As tested:

 import java.awt.*; import java.awt.event.*; import java.util.Random; import javax.swing.*; public class DieTest extends JFrame { DieTest() { setLayout(new FlowLayout()); add(new JDie()); add(new JDie()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new DieTest(); } }); } private static class JDie extends JPanel { private static final int SIDE = 32; private static final Random r = new Random(); private Color color; private int value = getValue(); private final Timer t = new Timer(500, null); JDie() { setBorder(BorderFactory.createEtchedBorder(color, color.darker())); value = getValue(); addMouseListener(new MouseAdapter() { @Override public void mousePressed(MouseEvent arg0) { value = getValue(); repaint(); } }); t.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { value = getValue(); repaint(); } }); t.start(); } @Override public Dimension getPreferredSize() { return new Dimension(SIDE * 7, SIDE * 7); } private int getValue() { color = Color.getHSBColor(r.nextFloat(), 1, 1); return r.nextInt(6) + 1; } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(color); switch (value) { case 1: g.fillRect(3 * SIDE, 3 * SIDE, SIDE, SIDE); break; case 2: g.fillRect(5 * SIDE, SIDE, SIDE, SIDE); g.fillRect(SIDE, 5 * SIDE, SIDE, SIDE); break; case 3: g.fillRect(5 * SIDE, SIDE, SIDE, SIDE); g.fillRect(SIDE, 5 * SIDE, SIDE, SIDE); g.fillRect(3 * SIDE, 3 * SIDE, SIDE, SIDE); break; case 4: g.fillRect(SIDE, SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, 5 * SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, SIDE, SIDE, SIDE); g.fillRect(SIDE, 5 * SIDE, SIDE, SIDE); break; case 5: g.fillRect(SIDE, SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, 5 * SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, SIDE, SIDE, SIDE); g.fillRect(SIDE, 5 * SIDE, SIDE, SIDE); g.fillRect(3 * SIDE, 3 * SIDE, SIDE, SIDE); break; case 6: g.fillRect(SIDE, SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, 5 * SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, SIDE, SIDE, SIDE); g.fillRect(SIDE, 5 * SIDE, SIDE, SIDE); g.fillRect(SIDE, 3 * SIDE, SIDE, SIDE); g.fillRect(5 * SIDE, 3 * SIDE, SIDE, SIDE); break; } } } } 
+4
source

All Articles