paintComponent是如何工作的?

时间:2013-03-21 10:10:19

标签: java swing paintcomponent

这可能是一个非常noob的问题。我刚刚开始学习Java

我不了解paintComponent方法的操作。我知道如果我想画一些东西,我必须覆盖paintComponent方法。

public void paintComponent(Graphics g)
{
   ...
}

但什么时候打电话?我从来没有看到像“object.paintComponent(g)”这样的东西,但它仍然是在程序运行时绘制的。

什么是Graphics参数?这个从哪里来?调用方法时必须提供参数。但正如我之前所说,似乎永远不会明确地调用此方法。那么谁提供这个参数呢?为什么我们必须将其转换为Graphics2D?

public void paintComponent(Graphics g)
{
    ...
    Graphics2D g2= (Graphics2D) g;
    ...
}

5 个答案:

答案 0 :(得分:35)

对你的问题的(非常)简短回答是paintComponent在需要时被称为“{1}}。”有时候将Java Swing GUI系统想象成一个“黑盒子”会更容易,因为在没有太多可见性的情况下处理大部分内部构件。

有许多因素可以决定何时需要重新绘制组件,包括移动,重新调整大小,更改焦点,被其他帧隐藏等等。其中许多事件都是自动检测到的,并且在确定需要进行该操作时会在内部调用paintComponent

我和Swing一起工作了很多年,我认为我永远不会直接调用paintComponent,甚至不会直接从其他东西调用它。我最接近的是使用repaint()方法以编程方式触发某些组件的重绘(我假设在下游调用正确的paintComponent方法。

根据我的经验,很少直接覆盖paintComponent。我承认存在需要这种粒度的自定义渲染任务,但Java Swing确实提供了一套(相当)强大的JComponents和Layouts,可以用来完成繁重的工作,而不必直接覆盖paintComponent。我想我的观点是确保在您尝试滚动自己的自定义渲染组件之前,您无法对本机JComponents和布局执行某些操作。

答案 1 :(得分:10)

你可以在这做两件事:

  1. 阅读Painting in AWT and Swing
  2. 使用调试器并在paintComponent方法中放置断点。然后向上移动堆栈跟踪,看看如何提供Graphics参数。
  3. 仅供参考,以下是我在最后发布的代码示例中得到的堆栈跟踪:

    Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 15 in TestPaint))  
        TestPaint.paintComponent(Graphics) line: 15 
        TestPaint(JComponent).paint(Graphics) line: 1054    
        JPanel(JComponent).paintChildren(Graphics) line: 887    
        JPanel(JComponent).paint(Graphics) line: 1063   
        JLayeredPane(JComponent).paintChildren(Graphics) line: 887  
        JLayeredPane(JComponent).paint(Graphics) line: 1063 
        JLayeredPane.paint(Graphics) line: 585  
        JRootPane(JComponent).paintChildren(Graphics) line: 887 
        JRootPane(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5228   
        RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482 
        RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413  
        RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206   
        JRootPane(JComponent).paint(Graphics) line: 1040    
        GraphicsCallback$PaintCallback.run(Component, Graphics) line: 39    
        GraphicsCallback$PaintCallback(SunGraphicsCallback).runOneComponent(Component, Rectangle, Graphics, Shape, int) line: 78    
        GraphicsCallback$PaintCallback(SunGraphicsCallback).runComponents(Component[], Graphics, int) line: 115 
        JFrame(Container).paint(Graphics) line: 1967    
        JFrame(Window).paint(Graphics) line: 3867   
        RepaintManager.paintDirtyRegions(Map<Component,Rectangle>) line: 781    
        RepaintManager.paintDirtyRegions() line: 728    
        RepaintManager.prePaintDirtyRegions() line: 677 
        RepaintManager.access$700(RepaintManager) line: 59  
        RepaintManager$ProcessingRunnable.run() line: 1621  
        InvocationEvent.dispatch() line: 251    
        EventQueue.dispatchEventImpl(AWTEvent, Object) line: 705    
        EventQueue.access$000(EventQueue, AWTEvent, Object) line: 101   
        EventQueue$3.run() line: 666    
        EventQueue$3.run() line: 664    
        AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]    
        ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 76    
        EventQueue.dispatchEvent(AWTEvent) line: 675    
        EventDispatchThread.pumpOneEventForFilters(int) line: 211   
        EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 128    
        EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 117   
        EventDispatchThread.pumpEvents(int, Conditional) line: 113  
        EventDispatchThread.pumpEvents(Conditional) line: 105   
        EventDispatchThread.run() line: 90  
    

    Graphics参数来自:

    RepaintManager.paintDirtyRegions(Map) line: 781 
    

    涉及的片段如下:

    Graphics g = JComponent.safelyGetGraphics(
                            dirtyComponent, dirtyComponent);
                    // If the Graphics goes away, it means someone disposed of
                    // the window, don't do anything.
                    if (g != null) {
                        g.setClip(rect.x, rect.y, rect.width, rect.height);
                        try {
                            dirtyComponent.paint(g); // This will eventually call paintComponent()
                        } finally {
                            g.dispose();
                        }
                    }
    

    如果您看一下它,您将看到它从JComponent本身(间接使用javax.swing.JComponent.safelyGetGraphics(Component, Component))检索图形,该图形本身最终从其第一个“重量级父级”获取它(剪切到组件边界)它自己从相应的原生资源中获取它。

    关于您必须将Graphics强制转换为Graphics2D这一事实,恰好在使用Window Toolkit时,Graphics实际上扩展了Graphics2D,但是你可以使用“不必”扩展Graphics的其他Graphics2D(它不会经常发生,但AWT / Swing允许你这样做)。

    import java.awt.Color;
    import java.awt.Graphics;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    class TestPaint extends JPanel {
    
        public TestPaint() {
            setBackground(Color.WHITE);
        }
    
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.drawOval(0, 0, getWidth(), getHeight());
        }
    
        public static void main(String[] args) {
            JFrame jFrame = new JFrame();
            jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jFrame.setSize(300, 300);
            jFrame.add(new TestPaint());
            jFrame.setVisible(true);
        }
    }
    

答案 2 :(得分:3)

GUI系统的内部调用该方法,并将Graphics参数作为可以绘制的图形上下文传递。

答案 3 :(得分:3)

调用object.paintComponent(g)是一个错误。

而是在创建面板时自动调用此方法。 paintComponent()方法也可以通过repaint()类中定义的Component方法显式调用。

调用repaint()的效果是Swing会自动清除面板上的图形并执行paintComponent方法重绘此面板上的图形。

答案 4 :(得分:0)

如果您希望任何以前的绘图在组件上永久存在,您可能需要重新定义方法 void paintComponent(Graphics g){}。您需要通过显式调用升序类的方法(如 super.painComponent();)来完成此操作。这样,任何时候 Java 需要使用该paintComponent 方法时,您都会保持所做的更改。

这是因为如果您不这样做,超类将通过简单地调用自己的方法来撤消您所做的一切,完全忽略任何更改。