简体   繁体   English

恢复(取消定义)我的JFrame时,我该如何重画?

[英]How do I redraw things when my JFrame is restored (deiconified)?

I have a very basic little JFrame with JToggleButtons and subclassed JPanels that know how to draw what I want them to draw. 我有一个非常基本的小JFrame,它带有JToggleButtons和子类化的JPanels,它们知道如何绘制我想要它们绘制的东西。 Selecting a button causes an oval to appear in the corresponding panel. 选择一个按钮会使椭圆形出现在相应的面板中。 Unselecting the buttons makes the drawings disappear. 取消选择按钮会使图形消失。

按下按钮,出现形状

Unfortunately, minimizing (iconifying) and then restoring (deiconifying) causes any drawn shapes to disappear. 不幸的是,最小化(图标化)然后还原(去图标化)会导致任何绘制的形状消失。 So I need to trigger redrawings manually. 因此,我需要手动触发重绘。 The problem is that I can only get the redrawing done (that is, I only see it) if I show a message box first. 问题是,如果我首先显示一个消息框,则只能完成重绘(即,我只能看到它)。

Here's the deiconify event for the JFrame: 这是JFrame的去图标化事件:

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();
}

This method goes over all of my buttons and asks for the redraws when necessary: 此方法遍历我所有的按钮,并在必要时要求重绘:

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

and here is my subclassed JPanel: 这是我的子类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();
    }
}

The window should automatically be repainted once it is restored by the RepaintManager . 该窗口一旦由RepaintManager恢复,应自动重新绘制。 The problem is you are not performing custom painting like you should... 问题是您没有像应该那样执行自定义绘画。

This is not how to do custom painting... 这不是定制绘画的方法...

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

getGraphics can return null and is, at best, a snapshot of the graphics state. getGraphics可以返回null ,并且充其量只能是图形状态的快照。

Painting in Swing can occur at any time for many different reasons, most of which you don't have control over (nor should you care). 由于许多不同的原因,可以随时进行Swing中的绘画,其中大多数是您无法控制的(也不在乎)。

Your job is simply to respond to these repaint requests and update your components state. 您的工作仅仅是响应这些重绘请求并更新组件状态。

Swing has a detailed paint chain which is called automatically and which you can use. Swing具有详细的绘制链,可以自动调用该链,您可以使用它。

You should be overriding paintComponent and performing all painting within this method 您应该重写paintComponent并在此方法中执行所有绘制

Take a look at Performing Custom Painting and Painting in AWT and Swing for more details 看一下在AWT和Swing执行自定义绘画绘画以获取更多详细信息

Firstly, for speed I would use double buffering. 首先,为了提高速度,我将使用双缓冲。 It's best to paint your graphics off screen and display them to the screen when the drawing has completed. 最好在屏幕上绘制图形,并在绘图完成后将其显示在屏幕上。 The below should sort you out. 以下应将您分类。

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);
    }
}

I'm just providing this answer so people can see what I ended up doing. 我只是提供此答案,以便人们可以看到我最终在做什么。 The major lesson pointed out by everyone was to use the component's paintComponent. 每个人都指出的主要教训是使用组件的paintComponent。 See the comments for issues that you might be experiencing yourself. 请参阅评论,以解决您可能遇到的问题。

Edit: Updated to reflect comments from MadProgrammer. 编辑:更新以反映来自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;    
    }
}

I subclassed the buttons too, so I can get its "ID" off of it from the event handler: 我也对按钮进行了子类化,因此可以从事件处理程序中获取按钮的“ ID”:

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;
    }
}

The JFrame code that makes the buttons: 制作按钮的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]);
        }
    }

And that's the only manual redrawing I have to do - resizing and reshowing and all those other fun things eventually hit up the paintComponent, and it just has to know if its button is pushed to know what to do. 那是我唯一要做的手动重绘-调整大小和重新显示,所有其他有趣的事情最终都击中了paintComponent,它只需要知道是否按下按钮即可知道该怎么做。 Super clean and just what I wanted. 超级干净,正是我想要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM