首页 > 解决方案 > When paintComponent() is invoked during a repaint process

问题描述

I'm following the Oracle Swing tutorial per advice from my last post about painting. Now I'm confused as to when and how the paintComponent() method is invoked.

Here's the class:

public class MyPanel extends JPanel
{

private int squareX=50;
private int squareY=50;
private int squareW=50;
private int squareH=20;
public MyPanel()
{
    setBorder(BorderFactory.createLineBorder(Color.BLACK));

    addMouseListener(new MouseAdapter() { 
        public void mousePressed(MouseEvent e) {
            moveSquare(e.getX(), e.getY());
        }
    });
    addMouseMotionListener(new MouseAdapter(){
        public void mouseDragged(MouseEvent e) {
            moveSquare(e.getX(), e.getY());
        }
    });
}

private void moveSquare(int x, int y)
{
    int OFFSET = 1;
    if((squareX!=x)||(squareY!=y))
    {
        repaint(squareX, squareY, squareW+OFFSET, squareH+OFFSET);
        squareX=x;
        squareY=y;
        repaint(squareX, squareY, squareW+OFFSET, squareH+OFFSET);
    }
}

public Dimension getPreferredSize()
{
    return new Dimension(250, 200);
}

protected void paintComponent(Graphics g)
{
    super.paintComponent(g);
    g.drawString("This is my custom panel!", 10, 20);
    g.setColor(Color.RED);
    g.fillRect(squareX, squareY, squareW, squareH);
    g.setColor(Color.BLACK);
    g.drawRect(squareX, squareY, squareW, squareH);
}
}

The tutorial says that both repaint methods are to redraw the previous mouse location as well as the new one. I get that, but where does paintComponent come in? Is it invoked when we say repaint? If so, why doesn't it draw a rectangle in the previous location as well?

标签: javaswing

解决方案


repaint() 方法将绘制请求传递给RepaintManager.

当短时间内收到多个请求时,RepaintManager会将两个单独的请求合并为一个请求,以提高绘制效率。

所以如果你有类似的东西:

repaint(5, 5, 20, 20);
...
repaint( 30, 30, 20, 20);

最终RepaintManager将它们合并为一个重新绘制的(5, 5, 45, 45). 这个更大的区域将包括两个单独请求的区域。

然后RepaintManager将调用paint()组件,该组件又调用paintComponent(...)带有裁剪区域的方法。然后该paintCompnent()方法将绘制剪切区域的背景(这将从背景中清除旧正方形),然后在新位置绘制正方形。

编辑:

repaint()方法不做任何绘画。The repaint()方法只是调用RepaintManager需要绘制的组件的建议区域。

  1. 第一个 repaint() 建议要绘制的区域。
  2. 第二个 repaint() 建议另一个要绘制的区域。

RepaintManager两个较小的区域合并为一个较大的区域。几毫秒后,它会告诉组件根据更大的区域重新绘制自己(这称为剪辑边界)。每次鼠标点击该paintComponent()方法只调用一次。

调用该paintComponent(...)方法时:

super.paintComponent(g);

上面的代码只是绘制了由 Graphics 对象的剪辑边界中指定的区域约束的组件的背景。背景颜色由 setBackground(...) 方法控制。

g.drawString("This is my custom panel!", 10, 20);
g.setColor(Color.RED);
g.fillRect(squareX, squareY, squareW, squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX, squareY, squareW, squareH);

然后,上面的代码将在清除的背景上绘制正方形。

将绘画代码更改为:

super.paintComponent(g);
System.out.println( g.getClipBounds() );

你会看到被重绘的组件的实际区域是两个较小区域的并集。

从理论上讲,绘制较小的区域比绘制较大的区域更有效。然而实际上,在这个简单的例子中没有必要。

更改代码如下:

//repaint(squareX, squareY, squareW+OFFSET, squareH+OFFSET);
squareX=x;
squareY=y;
//repaint(squareX, squareY, squareW+OFFSET, squareH+OFFSET);
repaint();

你仍然会得到相同的绘画结果(使用更简单的代码)。请注意,无论您单击何处,剪辑边界现在都是整个面板的大小。


推荐阅读