神秘(并发/组件绘图?)错误在非常简单的Swing骰子程序 [英] Mystery (concurrency/component drawing?) bug in very simple Swing dice program

查看:151
本文介绍了神秘(并发/组件绘图?)错误在非常简单的Swing骰子程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于可怜的问题标题,我很抱歉,因为这个错误的原因,不知道如何用这个问题。



学习基本Swing,并从在线图书简介此练习



我没有按照指示写信,而是试图这样做:




  • 有一个窗口,显示两个骰子的视觉表示

  • 当您点击其中一个骰子时,它会滚动并显示新值



我的实现:




  • 扩展JPanel的对象

  • 覆盖paintComponent方法绘制模具表示

  • 每次值只是为视觉线索更改时更改模具颜色

  • 添加了一个监听器,以便在按下鼠标时滚动骰子,然后

    重新绘制



错误是非常具体的:


  1. 运行DieTest主方法


  2. 点击第二个模具滚动

  3. 现在点击第一个模具滚动它
  4. b $ b

  5. 如果调整窗口大小,第二个die的值也会改变。
  6. >

如果我点击骰子滚动它们,之前我调整窗口大小,该错误不会出现。 ..



我想我在某个地方犯了一个基本的错误,只是伪装成这种奇怪的行为。



我试图尽可能地减少代码尽可能多的,它花了年龄只是出现在bug出现时和没有时:

  import java.awt。*; 
import java.awt.event。*;
import javax.swing。*;

class JDie extends JPanel {

private颜色;
private int value;

JDie(){

value = getValue();
color = Color.BLACK;

//添加监听器
addMouseListener(new MouseAdapter(){
@Override
public void mousePressed(MouseEvent arg0){
value = getValue ; //'roll'the die
repaint();
}
});
}

/ *私人辅助方法* /
private int getValue(){
int v =(int)(Math.random()* 6) 1;
//改变颜色只是为了显示
//的值已经改变
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);
}

//绘制模具的点数
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(color);


//绘制pips
//设置pip大小
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);

//如果我设置的大小小于JDie显示
//并在滚动骰子之前调整窗口大小
//然后bug出现... ?!
setSize(80,80);
//设置大小大于两个JDie
//和它工作正常无论你调整大小或不
// setSize(200,200);

setVisible(true);

}

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

@覆盖
public void run(){
new DieTest();
}

});
}

}



< ------- EDIT -----------------



再次运行代码,我已经注意到了它不会发生100%的时间,但是bug仍然存在。这里有一个gif,我只是它的bug,可能更好地说明问题:





当我再次点击第一个模具时,你可以清楚地看到原始颜色的原始值正在重绘。
当我调整窗口大小时,第二个die的值跳回到之前的值,它有...
我真的不明白这个...



---------------编辑2 ---------------------




  • 在另一台计算机(mac)上尝试了相同的代码,但无法复制
    的问题。

  • 编译并且在我的计算机上运行代码,而不是Eclipse,不能
    复制这个问题。

  • 使用Eclipse从命令行编译的代码,只有

  • 运行Eclipse中的代码,我仍然会在
    中遇到1个问题5-10次?如果它不显示在第一个通过,因为它是
    不显示。

    因此,似乎我的电脑设置也有一些影响,细节如下:




    • Windows 7 64位

    • Eclipse Kepler Service Release 1

    • Java Version 7 Update 51

    • Java SE开发套件7更新3(64位)



    这是一个棘手的,因为我知道更长时间知道它是我的代码或一些其他程序是导致的问题。作为一个newb我怎么知道是否任何未来的问题是我的错误的编程或其他的结果...令人沮丧。



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



    作为对并发方面的一个快速调查:
    我设置所有的实例字段to volatile
    我将所有的方法包括paintComponent设置为synchronized
    我删除了Math.random()调用(虽然我读到另一个线程说这是线程安全实现),并替换为实例随机对象



    我注意到的另一件事是,它似乎现在发生很少,现在约10分之一。我继续得到我的希望,它是固定的,然后下一次尝试再次错误。在我原来的程序,我在工作,它似乎更像是1在3(我完全改变了该程序,所以没有它手)。



    --------编辑4 ---------
    我想出了一个稍微更多的剥离版本,不再使用任何随机值,仍然产生视觉切换。使用此代码似乎更常发生:

      import java.awt。*; 
    import java.awt.event。*;
    import javax.swing。*;

    public class ColorPanelsWindow extends JFrame {

    class ColorPanel extends JPanel {

    //颜色从黑色开始
    //一旦更改应该永远不是
    //黑色再次
    私人颜色color = Color.BLACK;

    ColorPanel(){
    //添加侦听器
    //单击面板旋转颜色
    addMouseListener(new MouseAdapter(){
    @Override
    public void mousePressed(MouseEvent arg0){
    color = rotateColor();
    repaint();
    }
    });
    }
    //旋转颜色black / blue>红>绿色> 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());
    //必须设置大小,以使窗口太小
    //和两个ColorPanels重叠
    setSize(40,40);
    // setSize(300,200);

    setVisible(true);

    }

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

    @覆盖
    public void run(){
    new ColorPanelsWindow();
    }

    });
    }

    }

    p>


    • 首先必须重叠面板

    • 如果使用流布局,窗口,或任何方式
      ,阻止面板重叠初始,然后我不似乎
      得到的问题(或者也许只是发生少得多?)


    解决方案

    是的,是的,我确切地知道你在说什么。我对我的申请有同样的影响。



    虽然我真的很难找出原因,我 - 甚至现在 - 不知道它是否真的我认为这发生了,
    (太技术,不适合张贴在这里所有)我修复它,我甚至不知道为什么它的工作。



    因此,不是重新绘制组件本身,而是重新绘制其容器(或其容器的容器 - 如果仍然没有解决)。

      getParent()。repaint(); 

    希望有帮助。


    Sorry for the poor question title, I'm stumped as to the cause of this bug and didn't know how to phrase the question.

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

    I haven't followed the instructions to the letter and instead am trying to do this:

    • have a window that shows a visual representation of two dice
    • when you click on one of the dice, it 'rolls' and shows the new value

    My implementation:

    • a very basic JDie object that extends JPanel
    • overridden paintComponent method to draw the die representation
    • change die color every time value is changed just for a visual clue
    • added a listener to 'roll' the dice when mouse is pressed and then
      repaint

    The bug is quite specific:

    1. Run the DieTest main method
    2. Resize the window to fit the two die
    3. Click on the second die to roll it
    4. Now click on the first die to roll it
    5. The second die's value changes back to its original value
    6. If you resize the window the second die's value will also change back

    If I click on the dice to roll them, before I resize the window, the bug won't appear...

    I guess I'm making a basic mistake somewhere which has just disguised itself as this strange behaviour.

    I've tried to whittle down the code as much as I could, it took ages just working out when the bug appeared and when it didn't:

    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've now noticed it doesn't happen 100% of the time, but the bug is still there. Here's a gif that I just took of it bugging that might illustrate the problem better:

    You can clearly see the original value with the original color is being redrawn when I click the first die again. When I resize the window, the second die's value jumps back one, to the previous value it had... I really don't understand this...

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

    • Tried the same code on another computer (mac) but couldn't replicate the problem.
    • Compiled and ran the code on my computer outside of Eclipse, couldn't replicate the problem.
    • Ran the code that Eclipse had compiled from command line, only got the problem once, couldn't replicate it today.
    • Running the code from Eclipse, I still get the problem about 1 in 5-10 times? If it doesn't show up on the first 'pass' as it were, it doesn't show up at all. The gif illustrates it well.

    So it seems my computer set up has some bearing on this as well, details are:

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

    This is a tricky one as I know longer know whether it's my code or some other program's that is causing the problem. As a newb how can I know whether any future problems are a result of my bad programming or something else... frustrating.

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

    As a quick investigation into the concurrency side of things: I set all the instance fields to volatile I set all the methods including paintComponent to synchronized I removed the Math.random() call (although I read another thread saying that this was the thread safe implementation) and replaced it with instance Random objects instead

    Unfortunately I still get the visual switchback.

    Another thing I've noticed is that it seems to be happening a lot less often now about 1 in 10 times. I keep on getting my hopes up that it's fixed, and then the next attempt bugs again. In my original program I was working on, it seemed more like 1 in 3 (I've completely changed that program now so don't have it to hand).

    --------EDIT 4--------- I have come up with a slightly more stripped down version which no longer uses any random values and still produces the visual switchback. It seems to happen 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 couple of observations:

    • afaict the panels must be overlapping at first
    • if I use a flow layout, increase the size of the window, or any way that stops the panels from overlapping initially, then I don't seem to get the problem (or maybe it just happens much less often?)

    解决方案

    Yes, yes, I know exactly what you're talking about. I suffered from the same effect on my application.

    Although I had a really hard time figuring out the reason, which I'm - even now - not sure if it's indeed what I think; that's happening, (too technical and not appropriate to post that all here) I did fix it, and I don't even know why it works.

    So, instead of repainting the component itself, repaint its container (or its container's container - if still didn't fix).

    getParent().repaint();
    

    Hope that helps.

    这篇关于神秘(并发/组件绘图?)错误在非常简单的Swing骰子程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆