对于这个糟糕的问题标题感到抱歉,我对这个错误的原因感到困惑,并且不知道该怎么说这个问题。
我正在学习基本的Swing并从在线书籍Java编程入门中学习this exercise。
我没有按照信中的说明进行操作,而是尝试这样做:
我的实施:
该错误非常具体:
如果我点击骰子滚动它们,之前我调整窗口大小,则不会出现错误......
我想我在某个地方犯了一个基本的错误,这个错误只是伪装成这种奇怪的行为。
我试图尽可能地减少代码,但是当bug出现时以及没有出现时需要花费很长时间才能完成:
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 -----------------
再次运行代码,我现在已经注意到它不会100%发生,但是bug仍然存在。这是一个我刚刚接受它的gif,它可以更好地说明问题:
当我再次点击第一个骰子时,您可以清楚地看到原始颜色正在重新绘制的原始值。 当我调整窗口大小时,第二个骰子的值会跳回一个,达到之前的值...... 我真的不明白这个......
---------------编辑2 ---------------------
所以看来我的电脑设置也对此有所影响,详情如下:
这是一个棘手的问题,因为我知道更长时间是知道这是我的代码还是导致问题的其他程序。作为一个新手,我如何才能知道未来的问题是由于我糟糕的编程还是别的......令人沮丧。
----------编辑3 -----------
作为对并发方面的快速调查: 我将所有实例字段设置为volatile 我将包括paintComponent在内的所有方法都设置为synchronized 我删除了Math.random()调用(虽然我读了另一个线程说这是线程安全的实现)并用实例Random对象替换它
不幸的是,我仍然可以进行视觉转换。
我注意到的另一件事是它现在似乎发生的次数要少得多,现在大约十分之一。我一直希望它能够得到修复,然后下一次尝试再次出现问题。在我正在制作的原始程序中,它看起来更像是三分之一(我现在完全改变了程序,所以不要手工操作)。
--------编辑4 --------- 我提出了一个稍微简化的版本,不再使用任何随机值,仍然产生视觉转换。这段代码似乎经常发生:
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();
}
});
}
}
有几点意见:
答案 0 :(得分:4)
与Múna一样,我无法重现您的发现,但我有一些观察结果:
覆盖getPreferredSize()
以定义初始几何体。
定义强制几何关系的常量。
使用符合首选尺寸的layout,例如FlowLayout
要查看效果。
使用Color.getHSBColor()
获取各种饱和色调。
根据需要使用Random
的单个实例。
附录:问题的间歇性和平台可变性强烈暗示不正确的同步。在原始程序中,两个骰子共享由Random
拥有的Math
的单个静态实例;在下面的例子中,每个骰子都有自己的实例。另请注意,间接调整帧的大小会调用paintComponent()
。
经测试:
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;
}
}
}
}
答案 1 :(得分:2)
是的,是的,我确切地知道你在谈论什么。我的申请受到了同样的影响。
虽然我很难弄清楚原因,但我 - 现在 - 不确定这是不是我的想法;那发生了, (太技术性,不适合在这里发布所有内容)我做了修复它,我甚至不知道它为什么会起作用。
因此,重新绘制其容器(或其容器的容器 - 如果仍然没有修复),而不是重新绘制组件本身。
getParent().repaint();
希望有所帮助。