2013-06-26 14 views
0

我有一个非常基本的小JFrame JToggleButtons和子类JPanels,知道如何绘制我想让他们画的东西。选择一个按钮会导致椭圆出现在相应的面板中。取消选择按钮会使图形消失。当我的JFrame恢复(去除噪音)时,我该如何重绘东西?

Push a button, a shape appears

不幸的是,最小化(图标化),然后恢复(deiconifying)导致任何绘制的形状消失。所以我需要手动触发重绘。问题是如果我先显示一个消息框,我只能完成重绘(也就是说,我只有它)。

下面是对JFrame的deiconify事件:

private void formWindowDeiconified(java.awt.event.WindowEvent evt) 
{          
    //having this message makes everything work 
    JOptionPane.showMessageDialog(null, "Useless message this is."); 
    //but if I skip it, I'm SOL 
    //what's going on? 
    drawAll(); 
} 

这种方法越过我所有的按钮,并要求在必要时重绘:

public void drawAll() 
{ 
    for (int i=0; i<channels; i++) 
    { 
     if (buttons[i].isSelected()) 
     { 
      lightboxes[i].drawMe();    
     } 
    } 
} 

,这里是我的子类的JPanel:

class MyJPanel extends JPanel { 

    public void drawMe() 
    { 
     Graphics myGraphics = this.getGraphics(); 
     myGraphics.fillOval(0, 0, this.getWidth(), this.getHeight());  
    } 

    public void unDraw() 
    { 
     this.invalidate(); 
     this.repaint(); 
    } 
} 
+0

为了更快得到更好的帮助,请发布[SSCCE](http://sscce.org/)。 –

+0

我听说过这些,但我认为在这种情况下它并不实用。最终它变得无关紧要 - 整个过程就是我应该在组件的paintComponent事件中完成绘图,并让Swing处理细节。 –

回答

1

首先,速度我会使用双缓冲。最好将图形从屏幕上画下来,并在绘图完成时将它们显示在屏幕上。下面的内容应该让你感到满意。

public class MyPanel extends JPanel { 
    private BufferedImage buffer; 
    private Graphics2D canvas; 

    @Override 
    public void paintComponent(Graphics g) { 
     if(buffer == null) { 
      buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); 
      canvas = buffer.createGraphics(); 
     } 
     canvas.fillOval(0, 0, this.getWidth(), this.getHeight()); 
     g.drawImage(buffer, 0, 0, this); 
    } 
} 
+2

默认情况下,Swing组件是双缓冲的......所以现在是三重缓冲......我也可能是错的,但在某些系统上,不处理图形上下文可能会减慢渲染速度或阻止开始绘制(我认为这就是前段时间在Mac上有一个问题......) – MadProgrammer

+0

谢谢!除了MadProgrammer指出的教程之外,这个工作得很好。 (我给你的支票,因为你的答案有帮助我在一两分钟内解决我的问题的基本案例的代码,我没有停下来,教程也有帮助,但这是一个重要的起点) –

+0

确实Swing是双缓冲的,但AWT不是。 – Slihp

4

一旦窗口被0123恢复,窗口就会自动重新绘制。问题是你不喜欢你应该执行自画...

这不是如何做定制画...

public void drawMe() 
{ 
    Graphics myGraphics = this.getGraphics(); 
    myGraphics.fillOval(0, 0, this.getWidth(), this.getHeight());  
} 

getGraphics可以返回null是,充其量快照图形状态。

由于许多不同原因,Swing中的绘画可能随时出现,其中大多数您无法控制(也不应该关注)。

您的工作仅仅是响应这些重绘请求并更新组件状态。

Swing有一个详细的油漆链,它可以自动调用并可以使用。

你应该重写paintComponent这种方法

内执行所有绘画看看Performing Custom PaintingPainting in AWT and Swing更多细节

+0

谢谢。我在该教程中获得了非常丰富的经验。 –

1

我只是提供这个答案,所以人们可以看到我最终做了什么。大家指出的主要教训是使用组件的paintComponent。查看您可能遇到的问题的评论。

编辑:已更新以反映MadProgrammer的评论。

//with help from Slihp and MadProgrammer on StackOverflow 
//http://stackoverflow.com/q/17331986/1736461 
class MyJPanel extends JPanel { 

    private boolean buttonSelected = false; 

    @Override 
    public void paintComponent(Graphics g) { 
     //make sure the background gets painted (wacky results otherwise)   
     super.paintComponent(g); 

     //now if the corresponding toggle button is on, plop on the circle 
     if (buttonSelected) 
     { 
      g.fillOval(0, 0, this.getWidth(), this.getHeight()); 
     }   
    } 

    //an action listener for the button calls this 
    public void setButtonSelected(boolean buttonStateIn) 
    { 
     buttonSelected = buttonStateIn;  
    } 
} 

我子类的按钮太多,所以我可以从事件处理程序获得它的“身份证”关闭它:

class MyJToggleButton extends JToggleButton 
{  
    private int whoAmI; 

    public MyJToggleButton(int whoAmIn) 
    { 
     //the string given to the constructor becomes the button's label 
     //("1", "2", "3", etc..) 
     super(Integer.toString(whoAmIn + 1)); 
     whoAmI = whoAmIn;  
    } 

    public int getWho() 
    { 
     return whoAmI; 
    } 
} 

JFrame中的代码,使按键:

private void makeButtons(int howMany) 
    { 
     buttons = new MyJToggleButton[howMany]; 
     for (int i=0; i<howMany; i++) 
     { 
      buttons[i] = new MyJToggleButton(i); 
      buttons[i].addActionListener(new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent evt) { 
        //find out which button this is 
        MyJToggleButton button = (MyJToggleButton) evt.getSource(); 
        int which = button.getWho(); 
        //send the button state to the corresponding lightbox 
        lightboxes[which].setButtonSelected(button.isSelected()); 
        //trigger its redrawing 
        lightboxes[which].invalidate(); 
        lightboxes[which].repaint(); 
       } 
      }); 
      this.add(buttons[i]); 
     } 
    } 

这就是我必须做的唯一手动重绘 - 调整大小和重新显示以及所有其他有趣的事情最终会触发paintComponent,并且它只需知道它的按钮是否被推动知道该怎么做。超级干净,正是我想要的。

+0

不要忘记,Swing组件已经是双缓冲的。在某些操作系统的不处理图形上下文实际上可能会减缓渲染过程 – MadProgrammer

+0

您是否建议我不应该使用BufferedImage?我会在哪里处理图形上下文? –

+1

你不需要缓冲图像,Swing组件已经被双重缓冲。如果您要使用类似的东西,那么您应该在绘制方法结束时处理(缓冲图像的)图形上下文。这意味着您需要在开始时创建它; 2-监视组件大小的变化并相应地创建新的缓冲图像。永远不要处理你不会创建的图形上下文 – MadProgrammer

相关问题