JPanel将正确显示按钮,但不会正确显示自定义JComponent

问题描述:

我有一个延伸JComponent的类Cell。目标是显示一个网格单元格,并且每个单元格都能够处理它们自己的点击事件等。它基本上是一个平面按钮。JPanel将正确显示按钮,但不会正确显示自定义JComponent

当我将几个单元格添加到JPanel时,只显示其中一个单元格。如果使用相同的代码,我用按钮替换我的单元格,一切都按预期工作。

我错过了什么?

主要方法

public static void main(String[] args){ 
    JFrame f = new JFrame(); 
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    f.setSize(new Dimension(300,300)); 
    JPanel jp = new JPanel(); 
    jp.setLayout(new GridLayout(1, 3)); 
    if(true){//Use buttons instead of cells 
     jp.add(new JButton("Button 1")); 
     jp.add(new JButton("Button 2")); 
     jp.add(new JButton("Button 3")); 
    } 
    else{ //Use cells instead of buttons 
     Cell a = new Cell(10,0,0); 
     Cell b = new Cell(10,0,1); 
     Cell c = new Cell(10,0,2); 
     jp.add(a,0); 
     jp.add(b,1); 
     jp.add(c,2); 
    } 

    f.add(jp); 
    f.setVisible(true); 
    } 

Cell类

public class Cell extends JComponent{ 
    private static int numCells=0; 
    private Dimension size; 
    private int dt; 
    private int dl; 
    private Color color; 
    public Cell(int size, int dt, int dl){ 
     numCells++; 
     Random rand = new Random(); 
     this.size = new Dimension(size,size); 
     this.dt = dt; 
     this.dl = dl; 
     this.color = new Color(//Random color, but only in one :r, g, or b 
      (numCells%3==0)?rand.nextInt(255):0, 
      (numCells%3==1)?rand.nextInt(255):0, 
      (numCells%3==2)?rand.nextInt(255):0 
    ); 
     this.setPreferredSize(this.size); 
     this.setMaximumSize(this.size); 
     this.setMinimumSize(this.size); 
     this.setBackground(color); 
     this.setVisible(true); 
     this.setOpaque(true); 
    } 
    public void amClicked(){ 
     JOptionPane.showMessageDialog(this.getParent(), 
       this.toString()); 
    } 

    public String toString(){ 
     return ""+dt+","+dl; 
    } 
    public void paintComponent(Graphics g){ 
       Graphics ng = g.create(); 
    try{ 
     super.paintComponent(ng); 
     ng.setColor(color); 
     System.out.println(String.format("%d,%d,%d,%d(%d,%d,%d)", 
       this.getX(), this.getY(), this.getWidth(), this.getHeight(), 
       this.color.getRed(),this.color.getGreen(),this.color.getBlue())); 
     ng.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight()); 
    } 
    finally{ 
     ng.dispose(); 
    } 
    } 


} 

您正在添加3个组件,但只有一个被涂成黑色。你为什么要使用的getX和这里的getY

 g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight()); 

:添加红线边框你的细胞,看看:

public Cell(int size, int dt, int dl) { 
    numCells++; 
    //.... code deleted 

    // !!this.color = new Color(Color.BLACK); // *** won't compile! 
    color = Color.black; 

    //.... code deleted 

    this.setOpaque(true); 
    setBorder(BorderFactory.createLineBorder(Color.red, 2)); // **** add this 
    } 

编辑:这行看起来粗略的给我吗?这些方法返回相对于容器而不是单元格的位置信息,但是您使用它来绘制相对于单元格而不是容器的位置,因此黑色矩形将从可见单元格中绘制出来,并且这可能不是你想要什么。也许你需要使用0代替:

 g.fillRect(0, 0, this.getWidth(), this.getHeight()); 
+0

我原本有随机颜色,但希望颜色只能是红色,只有绿色或只有蓝色。为了避免混淆,我在发布之前删除了这段代码。我把它放回来,因为“避免混淆”部分不起作用 – amccormack 2011-02-05 21:39:53

在此代码的一个明显缺陷是,将更改应用于图形对象,通过的setColor()的事实,但你不会把他们推回去。

这在Javadocs明确规定:

如果重写此在子类中,你应该不要对图形编辑通过的永久性变化。

一般的解决方法是将催生一个新的Graphics对象关你通过Graphics.create()得到的参数,包装你的代码try-finally块内,并且配置在finally子句中新的图形通过Graphics.dispose()对象。